diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/config/GeoEngine.ini b/L2J_Mobius_1.0_Ertheia/dist/game/config/GeoEngine.ini index 609e8a89a7..e740c678dd 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_1.0_Ertheia/dist/game/config/GeoEngine.ini @@ -6,33 +6,44 @@ # at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ 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 + # ================================================================= -# Path finding +# Pathfinding # ================================================================= # 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 +# an alternative path (e.g. walk around obstacle), default: True +PathFinding = True -# Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 -PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 +# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 -# Weight for nodes without obstacles far from walls -LowWeight = 0.5 +# Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 +MoveWeight = 10 +MoveWeightDiag = 14 -# Weight for nodes near walls -MediumWeight = 2 +# 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 -# Weight for nodes with obstacles -HighWeight = 3 +# 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. -# Default: LowWeight * sqrt(2) -DiagonalWeight = 0.707 +# Maximum number of generated nodes per one path-finding process, default 3500 +MaxIterations = 3500 # ================================================================= -# Other +# Line of Sight # ================================================================= -# Correct player Z after falling through the ground. -CorrectPlayerZ = False +# 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 diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/geodata/L2D_GeoEngine.patch b/L2J_Mobius_1.0_Ertheia/dist/game/data/geodata/L2D_GeoEngine.patch deleted file mode 100644 index f8577e3a0e..0000000000 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/geodata/L2D_GeoEngine.patch +++ /dev/null @@ -1,6035 +0,0 @@ -Index: dist/game/GeoDataConverter.bat -=================================================================== ---- dist/game/GeoDataConverter.bat (nonexistent) -+++ dist/game/GeoDataConverter.bat (working copy) -@@ -0,0 +1,6 @@ -+@echo off -+title L2D geodata converter -+ -+java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter -+ -+pause -Index: dist/game/GeoDataConverter.sh -=================================================================== ---- dist/game/GeoDataConverter.sh (nonexistent) -+++ dist/game/GeoDataConverter.sh (working copy) -@@ -0,0 +1,4 @@ -+#! /bin/sh -+ -+java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 -+ -Index: dist/game/config/GeoEngine.ini -=================================================================== ---- dist/game/config/GeoEngine.ini (revision 8409) -+++ dist/game/config/GeoEngine.ini (working copy) -@@ -1,6 +1,10 @@ - # ================================================================= - # Geodata - # ================================================================= -+# Because of real-time performance we are using geodata files only in -+# diagonal L2D format now (using filename e.g. 22_16.l2d). -+# L2D geodata can be obtained by conversion of existing L2J or L2OFF geodata. -+# Launch "GeoDataConverter.bat/sh" and follow instructions to start the conversion. - - # 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/ -@@ -13,6 +17,16 @@ - CoordSynchronize = 2 - - # ================================================================= -+# Path checking -+# ================================================================= -+ -+# 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 finding - # ================================================================= - -@@ -23,19 +37,23 @@ - # Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - --# Weight for nodes without obstacles far from walls --LowWeight = 0.5 -+# Base path weight, when moving from one node to another on axis direction, default: 10 -+BaseWeight = 10 - --# Weight for nodes near walls --MediumWeight = 2 -+# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -+DiagonalWeight = 14 - --# Weight for nodes with obstacles --HighWeight = 3 -+# When movement flags of target node is blocked to any direction, multiply movement weight by this multiplier. -+# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 10 -+ObstacleMultiplier = 10 - --# Weight for diagonal movement. --# Default: LowWeight * sqrt(2) --DiagonalWeight = 0.707 -+# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 20 -+# For proper function must be higher than BaseWeight and/or DiagonalWeight. -+HeuristicWeight = 20 - -+# Maximum number of generated nodes per one path-finding process, default 3500 -+MaxIterations = 3500 -+ - # ================================================================= - # Other - # ================================================================= -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (working copy) -@@ -55,9 +55,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -@@ -73,9 +72,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (working copy) -@@ -18,11 +18,11 @@ - - import java.util.List; - --import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -+import org.l2jmobius.gameserver.geoengine.GeoEngine; - import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -+import org.l2jmobius.gameserver.network.SystemMessageId; - import org.l2jmobius.gameserver.util.BuilderUtil; - - public class AdminPathNode implements IAdminCommandHandler -@@ -37,29 +37,30 @@ - { - if (command.equals("admin_path_find")) - { -- if (!Config.PATHFINDING) -- { -- BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); -- return true; -- } - if (activeChar.getTarget() != null) - { -- final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); -+ final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); - if (path == null) - { -- BuilderUtil.sendSysMessage(activeChar, "No Route!"); -- return true; -+ BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); - } -- for (AbstractNodeLoc a : path) -+ else - { -- BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); -+ for (Location point : path) -+ { -+ BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); -+ } - } - } - else - { -- BuilderUtil.sendSysMessage(activeChar, "No Target!"); -+ activeChar.sendPacket(SystemMessageId.INVALID_TARGET); - } - } -+ else -+ { -+ return false; -+ } - return true; - } - -Index: java/org/l2jmobius/Config.java -=================================================================== ---- java/org/l2jmobius/Config.java (revision 8409) -+++ java/org/l2jmobius/Config.java (working copy) -@@ -32,7 +32,6 @@ - import java.net.UnknownHostException; - import java.nio.charset.StandardCharsets; - import java.nio.file.Files; --import java.nio.file.Path; - import java.nio.file.Paths; - import java.time.Duration; - import java.util.ArrayList; -@@ -966,14 +965,17 @@ - // -------------------------------------------------- - // GeoEngine - // -------------------------------------------------- -- public static Path GEODATA_PATH; -+ public static String GEODATA_PATH; -+ public static int COORD_SYNCHRONIZE; -+ public static int PART_OF_CHARACTER_HEIGHT; -+ public static int MAX_OBSTACLE_HEIGHT; - public static boolean PATHFINDING; - public static String PATHFIND_BUFFERS; -- public static float LOW_WEIGHT; -- public static float MEDIUM_WEIGHT; -- public static float HIGH_WEIGHT; -- public static float DIAGONAL_WEIGHT; -- public static int COORD_SYNCHRONIZE; -+ public static int BASE_WEIGHT; -+ public static int DIAGONAL_WEIGHT; -+ public static int HEURISTIC_WEIGHT; -+ public static int OBSTACLE_MULTIPLIER; -+ public static int MAX_ITERATIONS; - public static boolean CORRECT_PLAYER_Z; - - /** Attribute System */ -@@ -2568,14 +2570,17 @@ - - // Load GeoEngine config file (if exists) - final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); -- GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); -+ GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); -+ COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); -+ MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); - PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -- LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); -- MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); -- HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); -- DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); -- COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); -+ DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); -+ OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); -+ HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); -+ MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); - CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); - - // Load AllowedPlayerRaces config file (if exists) -Index: java/org/l2jmobius/gameserver/geoengine/GeoEngine.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (revision 8435) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (working copy) -@@ -16,100 +16,90 @@ - */ - package org.l2jmobius.gameserver.geoengine; - --import java.io.IOException; -+import java.io.File; - import java.io.RandomAccessFile; - import java.nio.ByteOrder; --import java.nio.channels.FileChannel.MapMode; --import java.nio.file.Files; --import java.nio.file.Path; --import java.util.concurrent.atomic.AtomicReferenceArray; --import java.util.logging.Level; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.List; - import java.util.logging.Logger; - - import org.l2jmobius.Config; - import org.l2jmobius.gameserver.data.xml.DoorData; - import org.l2jmobius.gameserver.data.xml.FenceData; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; --import org.l2jmobius.gameserver.geoengine.geodata.IRegion; --import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; --import org.l2jmobius.gameserver.geoengine.geodata.Region; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.World; - import org.l2jmobius.gameserver.model.WorldObject; -+import org.l2jmobius.gameserver.model.actor.Creature; - import org.l2jmobius.gameserver.model.instancezone.Instance; --import org.l2jmobius.gameserver.model.interfaces.ILocational; --import org.l2jmobius.gameserver.util.GeoUtils; --import org.l2jmobius.gameserver.util.LinePointIterator; --import org.l2jmobius.gameserver.util.LinePointIterator3D; -+import org.l2jmobius.gameserver.util.MathUtil; - - /** -- * @author -Nemesiss-, HorridoJoho -+ * @author Hasha - */ - public class GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); -+ protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - -- private static final int WORLD_MIN_X = -655360; -- private static final int WORLD_MIN_Y = -589824; -- private static final int WORLD_MIN_Z = -16384; -+ private final ABlock[][] _blocks; -+ private final BlockNull _nullBlock; - -- /** 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; -- -- /** The regions array */ -- private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); -- -- private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; -- private static final int ELEVATED_SEE_OVER_DISTANCE = 2; -- private static final int MAX_SEE_OVER_HEIGHT = 48; -- -- protected GeoEngine() -+ /** -+ * GeoEngine constructor. Loads all geodata files of chosen geodata format. -+ */ -+ public GeoEngine() - { - LOGGER.info("GeoEngine: Initializing..."); -- for (int i = 0; i < _regions.length(); i++) -- { -- _regions.set(i, NullRegion.INSTANCE); -- } - -+ // initialize block container -+ _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; -+ -+ // load null block -+ _nullBlock = new BlockNull(); -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files according to geoengine config setup - int loaded = 0; -- try -+ long fileSize = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { -- for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { -- for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) -+ final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); -+ if (f.exists() && !f.isDirectory()) - { -- final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); -- if (Files.exists(geoFilePath)) -+ // region file is load-able, try to load it -+ if (loadGeoBlocks(rx, ry)) - { -- try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) -- { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -- loaded++; -- } -+ loaded++; -+ fileSize += f.length(); - } - } -+ else -+ { -+ // region file is not load-able, load null blocks -+ loadNullBlocks(rx, ry); -+ } - } - } -- catch (Exception e) -+ -+ LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -+ if (loaded > 0) - { -- LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); -- System.exit(1); -+ LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); - } - -- LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -- -- // Avoid wrong configs when no files are loaded. -+ // avoid wrong configs when no files are loaded - if (loaded == 0) - { - if (Config.PATHFINDING) -@@ -123,619 +113,820 @@ - LOGGER.info("GeoEngine: Forcing CoordSynchronize setting to -1."); - } - } -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); - } - - /** -- * @param geoX -- * @param geoY -- * @return the region -+ * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. -+ * @return boolean : True, when geodata file was loaded without problem. - */ -- private IRegion getRegion(int geoX, int geoY) -+ private final boolean loadGeoBlocks(int regionX, int regionY) - { -- final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); -- if ((region < 0) || (region >= _regions.length())) -+ final String filename = String.format(GeoFormat.L2D.getFilename(), regionX, regionY); -+ -+ // standard load -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) - { -- return null; -+ // initialize file buffer -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, GeoFormat.L2D); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ } -+ -+ // check data consistency -+ if (buffer.remaining() > 0) -+ { -+ LOGGER.warning("GeoEngine: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ } -+ -+ // loading was successful -+ return true; - } -- return _regions.get(region); -- } -- -- /** -- * @param filePath -- * @param regionX -- * @param regionY -- * @throws IOException -- */ -- 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")) -+ catch (Exception e) - { -- _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -+ // an error occured while loading, load null blocks -+ LOGGER.warning("GeoEngine: Error while loading " + filename + " region file."); -+ LOGGER.warning(e.getMessage()); -+ -+ // replace whole region file with null blocks -+ loadNullBlocks(regionX, regionY); -+ -+ // loading was not successful -+ return false; - } - } - - /** -- * @param regionX -- * @param regionY -+ * Loads null blocks. Used when no region file is detected or an error occurs during loading. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. - */ -- public void unloadRegion(int regionX, int regionY) -+ private final void loadNullBlocks(int regionX, int regionY) - { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); -- } -- -- /** -- * @param geoX -- * @param geoY -- * @return if geodata exist -- */ -- public boolean hasGeoPos(int geoX, int geoY) -- { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // load all null blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { -- return false; -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[blockX + ix][blockY + iy] = _nullBlock; -+ } - } -- return region.hasGeo(); - } - -+ // GEODATA - GENERAL -+ - /** -- * Checks the specified position for available geodata. -- * @param x the world x -- * @param y the world y -- * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise -+ * Converts world X to geodata X. -+ * @param worldX -+ * @return int : Geo X - */ -- public boolean hasGeo(int x, int y) -+ public static int getGeoX(int worldX) - { -- return hasGeoPos(getGeoX(x), getGeoY(y)); -+ return (MathUtil.limit(worldX, World.MAP_MIN_X, World.MAP_MAX_X) - World.MAP_MIN_X) >> 4; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe check -+ * Converts world Y to geodata Y. -+ * @param worldY -+ * @return int : Geo Y - */ -- public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) -+ public static int getGeoY(int worldY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return true; -- } -- return region.checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(worldY, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.MAP_MIN_Y) >> 4; - } - - /** -+ * Converts geodata X to world X. - * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe anti-corner cut -+ * @return int : World X - */ -- public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) -+ public static int getWorldX(int geoX) - { -- boolean can = true; -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- return can && checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(geoX, 0, GeoStructure.GEO_CELLS_X) << 4) + World.MAP_MIN_X + 8; - } - - /** -- * @param geoX -+ * Converts geodata Y to world Y. - * @param geoY -- * @param worldZ -- * @return the nearest Z value -+ * @return int : World Y - */ -- public int getNearestZ(int geoX, int geoY, int worldZ) -+ public static int getWorldY(int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return worldZ; -- } -- return region.getNearestZ(geoX, geoY, worldZ); -+ return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next lower Z value -+ * Returns block of geodata on given coordinates. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return {@link ABlock} : Block of geodata. - */ -- public int getNextLowerZ(int geoX, int geoY, int worldZ) -+ private final ABlock getBlock(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final int x = geoX / GeoStructure.BLOCK_CELLS_X; -+ final int y = geoY / GeoStructure.BLOCK_CELLS_Y; -+ -+ // if x or y is out of array return null -+ if ((x > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) - { -- return worldZ; -+ return _blocks[x][y]; - } -- return region.getNextLowerZ(geoX, geoY, worldZ); -+ return null; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next higher Z value -+ * Check if geo coordinates has geo. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return boolean : True, if given geo coordinates have geodata - */ -- public int getNextHigherZ(int geoX, int geoY, int worldZ) -+ public boolean hasGeoPos(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final ABlock block = getBlock(geoX, geoY); -+ if (block == null) // null block check - { -- return worldZ; -+ // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) -+ // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); -+ return false; - } -- return region.getNextHigherZ(geoX, geoY, worldZ); -+ return block.hasGeoPos(); - } - - /** -- * Gets the Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the height of cell, which is closest to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, closest to given coordinates. - */ -- public int getHeight(int x, int y, int z) -+ public short getHeightNearest(int geoX, int geoY, int worldZ) - { -- return getNearestZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getHeightNearest(geoX, geoY, worldZ) : (short) worldZ; - } - - /** -- * Gets the next lower Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the NSWE flag byte of cell, which is closes to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. - */ -- public int getLowerHeight(int x, int y, int z) -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) - { -- return getNextLowerZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getNsweNearest(geoX, geoY, worldZ) : (byte) 0xFF; - } - - /** -- * Gets the next higher Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Check if world coordinates has geo. -+ * @param worldX : World X -+ * @param worldY : World Y -+ * @return boolean : True, if given world coordinates have geodata - */ -- public int getHigherHeight(int x, int y, int z) -+ public boolean hasGeo(int worldX, int worldY) - { -- return getNextHigherZ(getGeoX(x), getGeoY(y), z); -+ return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); - } - - /** -- * @param worldX -- * @return the geo X -+ * Returns closest Z coordinate according to geodata. -+ * @param worldX : world x -+ * @param worldY : world y -+ * @param worldZ : world z -+ * @return short : nearest Z coordinates according to geodata - */ -- public int getGeoX(int worldX) -+ public short getHeight(int worldX, int worldY, int worldZ) - { -- return (worldX - WORLD_MIN_X) / 16; -+ return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - -- /** -- * @param worldY -- * @return the geo Y -- */ -- public int getGeoY(int worldY) -- { -- return (worldY - WORLD_MIN_Y) / 16; -- } -+ // PATHFINDING - - /** -- * @param worldZ -- * @return the geo Z -+ * Check line of sight from {@link WorldObject} to {@link WorldObject}. -+ * @param origin : The origin object. -+ * @param target : The target object. -+ * @return {@code boolean} : True if origin can see target - */ -- public int getGeoZ(int worldZ) -+ public boolean canSeeTarget(WorldObject origin, WorldObject target) - { -- return (worldZ - WORLD_MIN_Z) / 16; -- } -- -- /** -- * @param geoX -- * @return the world X -- */ -- public int getWorldX(int geoX) -- { -- return (geoX * 16) + WORLD_MIN_X + 8; -- } -- -- /** -- * @param geoY -- * @return the world Y -- */ -- public int getWorldY(int geoY) -- { -- return (geoY * 16) + WORLD_MIN_Y + 8; -- } -- -- /** -- * @param geoZ -- * @return the world Z -- */ -- public int getWorldZ(int geoZ) -- { -- return (geoZ * 16) + WORLD_MIN_Z + 8; -- } -- -- /** -- * Can see target. Doors as target always return true. Checks doors between. -- * @param cha the character -- * @param target the target -- * @return {@code true} if the character can see the target (LOS), {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, WorldObject target) -- { -- if (target.isDoor()) -+ if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) - { -- // Can always see doors. - return true; - } -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param cha the character -- * @param worldPosition the world position -- * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) -- { -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param world -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @param tworld the target's instanceId -- * @return -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) -- { -- return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instance -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) -- { -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) -+ -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = target.getX(); -+ final int ty = target.getY(); -+ final int tz = target.getZ(); -+ -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } -- return canSeeTarget(x, y, z, tx, ty, tz); -- } -- -- /** -- * @param prevX -- * @param prevY -- * @param prevGeoZ -- * @param curX -- * @param curY -- * @param nswe -- * @return the LOS Z value -- */ -- private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) -- { -- if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { -- throw new RuntimeException("Multiple directions!"); -+ return false; - } - -- if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- return getNearestZ(curX, curY, prevGeoZ); -+ return true; - } - -- return getNextHigherZ(curX, curY, prevGeoZ); -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) -+ { -+ return true; -+ } -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) -+ { -+ return goz == gtz; -+ } -+ -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) -+ { -+ oheight = ((Creature) origin).getCollisionHeight() * 2; -+ } -+ -+ double theight = 0; -+ if (target.isCreature()) -+ { -+ theight = ((Creature) target).getCollisionHeight() * 2; -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); - } - - /** -- * Can see target. Does not check doors between. -- * @param xValue the x coordinate -- * @param yValue the y coordinate -- * @param zValue the z coordinate -- * @param txValue the target's x coordinate -- * @param tyValue the target's y coordinate -- * @param tzValue the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -+ * Check line of sight from {@link WorldObject} to {@link Location}. -+ * @param origin : The origin object. -+ * @param position : The target position. -+ * @return {@code boolean} : True if object can see position - */ -- public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) -+ public boolean canSeeTarget(WorldObject origin, Location position) - { -- int x = xValue; -- int y = yValue; -- int tx = txValue; -- int ty = tyValue; -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = position.getX(); -+ final int ty = position.getY(); -+ final int tz = position.getZ(); - -- int geoX = getGeoX(x); -- int geoY = getGeoY(y); -- int tGeoX = getGeoX(tx); -- int tGeoY = getGeoY(ty); -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) -+ { -+ return false; -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) -+ { -+ return false; -+ } - -- int z = getNearestZ(geoX, geoY, zValue); -- int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- // Fastpath. -- if ((geoX == tGeoX) && (geoY == tGeoY)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- if (hasGeoPos(tGeoX, tGeoY)) -- { -- return z == tz; -- } - return true; - } - -- if (tz > z) -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) - { -- int tmp = tx; -- tx = x; -- x = tmp; -- -- tmp = ty; -- ty = y; -- y = tmp; -- -- tmp = tz; -- tz = z; -- z = tmp; -- -- tmp = tGeoX; -- tGeoX = geoX; -- geoX = tmp; -- -- tmp = tGeoY; -- tGeoY = geoY; -- geoY = tmp; -+ return goz == gtz; - } - -- final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); -- // First point is guaranteed to be available, skip it, we can always see our own position. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- final int prevZ = pointIter.z(); -- int prevGeoZ = prevZ; -- int ptIndex = 0; -- while (pointIter.next()) -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight(); -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); -+ } -+ -+ /** -+ * Simple check for origin to target visibility. -+ * @param goxValue : origin X geodata coordinate -+ * @param goyValue : origin Y geodata coordinate -+ * @param gozValue : origin Z geodata coordinate -+ * @param oheight : origin height (if instance of {@link Character}) -+ * @param gtxValue : target X geodata coordinate -+ * @param gtyValue : target Y geodata coordinate -+ * @param gtzValue : target Z geodata coordinate -+ * @param theight : target height (if instance of {@link Character}) -+ * @param instance -+ * @return {@code boolean} : True, when target can be seen. -+ */ -+ private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) -+ { -+ int goz = gozValue; -+ int gtz = gtzValue; -+ int gox = goxValue; -+ int goy = goyValue; -+ int gtx = gtxValue; -+ int gty = gtyValue; -+ -+ // get line of sight Z coordinates -+ double losoz = goz + ((oheight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ double lostz = gtz + ((theight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ -+ // get X delta and signum -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirox = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; -+ final byte dirtx = sx > 0 ? GeoStructure.CELL_FLAG_W : GeoStructure.CELL_FLAG_E; -+ -+ // get Y delta and signum -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte diroy = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ final byte dirty = sy > 0 ? GeoStructure.CELL_FLAG_N : GeoStructure.CELL_FLAG_S; -+ -+ // get Z delta -+ final int dm = Math.max(dx, dy); -+ final double dz = (lostz - losoz) / dm; -+ -+ // get direction flag for diagonal movement -+ final byte diroxy = getDirXY(dirox, diroy); -+ final byte dirtxy = getDirXY(dirtx, dirty); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte diro; -+ byte dirt; -+ -+ // initialize node values -+ int nox = gox; -+ int noy = goy; -+ int ntx = gtx; -+ int nty = gty; -+ byte nsweo = getNsweNearest(gox, goy, goz); -+ byte nswet = getNsweNearest(gtx, gty, gtz); -+ -+ // loop -+ ABlock block; -+ int index; -+ for (int i = 0; i < ((dm + 1) / 2); i++) -+ { -+ // reset direction flag -+ diro = 0; -+ dirt = 0; - -- if ((curX == prevX) && (curY == prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- continue; -+ // calculate next point XY coordinates -+ d -= dy; -+ d += dx; -+ nox += sx; -+ ntx -= sx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroxy; -+ dirt |= dirtxy; - } -+ else if (e2 > -dy) -+ { -+ // calculate next point X coordinate -+ d -= dy; -+ nox += sx; -+ ntx -= sx; -+ diro |= dirox; -+ dirt |= dirtx; -+ } -+ else if (e2 < dx) -+ { -+ // calculate next point Y coordinate -+ d += dx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroy; -+ dirt |= dirty; -+ } - -- final int beeCurZ = pointIter.z(); -- int curGeoZ = prevGeoZ; -- -- // Check if the position has geodata. -- if (hasGeoPos(curX, curY)) - { -- final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); -- curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); -- int maxHeight; -- if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) -+ // get block of the next cell -+ block = getBlock(nox, noy); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nsweo & diro) == 0) - { -- maxHeight = z + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); - } - else - { -- maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); - } - -- boolean canSeeThrough = false; -- if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) -+ // layer does not exist, return -+ if (index == -1) - { -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else -- { -- canSeeThrough = true; -- } -+ return false; - } - -- if (!canSeeThrough) -+ // get layer and next line of sight Z coordinate -+ goz = block.getHeight(index); -+ losoz += dz; -+ -+ // perform line of sight check, return when fails -+ if ((goz - losoz) > Config.MAX_OBSTACLE_HEIGHT) - { - return false; - } -+ -+ // get layer nswe -+ nsweo = block.getNswe(index); - } -+ { -+ // get block of the next cell -+ block = getBlock(ntx, nty); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nswet & dirt) == 0) -+ { -+ index = block.getIndexAbove(ntx, nty, gtz - GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ else -+ { -+ index = block.getIndexBelow(ntx, nty, gtz + GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ -+ // layer does not exist, return -+ if (index == -1) -+ { -+ return false; -+ } -+ -+ // get layer and next line of sight Z coordinate -+ gtz = block.getHeight(index); -+ lostz -= dz; -+ -+ // perform line of sight check, return when fails -+ if ((gtz - lostz) > Config.MAX_OBSTACLE_HEIGHT) -+ { -+ return false; -+ } -+ -+ // get layer nswe -+ nswet = block.getNswe(index); -+ } - -- prevX = curX; -- prevY = curY; -- prevGeoZ = curGeoZ; -- ++ptIndex; -+ // update coords -+ gox = nox; -+ goy = noy; -+ gtx = ntx; -+ gty = nty; - } - -- return true; -+ // when iteration is completed, compare final Z coordinates -+ return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); - } - - /** -- * Move check. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param zValue the z coordinate -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tzValue the target's z coordinate -- * @param instance the instance -- * @return the last Location (x,y,z) where player can walk - just before wall -+ * Check movement from coordinates to coordinates. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {code boolean} : True if target coordinates are reachable from origin coordinates - */ -- public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) -+ public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- final int geoX = getGeoX(x); -- final int geoY = getGeoY(y); -- final int z = getNearestZ(geoX, geoY, zValue); -- final int tGeoX = getGeoX(tx); -- final int tGeoY = getGeoY(ty); -- final int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } -- if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = z; -+ // perform geodata check -+ final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); -+ return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); -+ } -+ -+ /** -+ * Check movement from origin to target. Returns last available point in the checked path. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {@link Location} : Last point where object can walk (just before wall) -+ */ -+ public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) -+ { -+ return new Location(ox, oy, oz); -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) -+ { -+ return new Location(ox, oy, oz); -+ } - -- while (pointIter.next()) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -- -- if (hasGeoPos(prevX, prevY)) -- { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -- { -- // Can't move, return previous location. -- return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); -- } -- } -- -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return new Location(tx, ty, tz); - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != tz)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- // Different floors, return start location. -- return new Location(x, y, z); -+ return new Location(tx, ty, tz); - } - -- return new Location(tx, ty, tz); -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) -+ { -+ return new Location(tx, ty, tz); -+ } -+ -+ // perform geodata check -+ return checkMove(gox, goy, goz, gtx, gty, gtz, instance); - } - - /** -- * Checks if its possible to move from one location to another. -- * @param fromX the X coordinate to start checking from -- * @param fromY the Y coordinate to start checking from -- * @param fromZvalue the Z coordinate to start checking from -- * @param toX the X coordinate to end checking at -- * @param toY the Y coordinate to end checking at -- * @param toZvalue the Z coordinate to end checking at -- * @param instance the instance -- * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise -+ * With this method you can check if a position is visible or can be reached by beeline movement.
-+ * Target X and Y reachable and Z is on same floor: -+ *
    -+ *
  • Location of the target with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y reachable but Z is on another floor: -+ *
    -+ *
  • Location of the origin with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y not reachable: -+ *
    -+ *
  • Last accessible location in destination to target.
  • -+ *
-+ * @param gox : origin X geodata coordinate -+ * @param goy : origin Y geodata coordinate -+ * @param goz : origin Z geodata coordinate -+ * @param gtx : target X geodata coordinate -+ * @param gty : target Y geodata coordinate -+ * @param gtz : target Z geodata coordinate -+ * @param instance -+ * @return {@link GeoLocation} : The last allowed point of movement. - */ -- public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) -+ protected final GeoLocation checkMove(int gox, int goy, int goz, int gtx, int gty, int gtz, Instance instance) - { -- final int geoX = getGeoX(fromX); -- final int geoY = getGeoY(fromY); -- final int fromZ = getNearestZ(geoX, geoY, fromZvalue); -- final int tGeoX = getGeoX(toX); -- final int tGeoY = getGeoY(toY); -- final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); -- -- if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) -+ if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } -- if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) -+ if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = fromZ; -+ // get X delta, signum and direction flag -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirX = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; - -- while (pointIter.next()) -+ // get Y delta, signum and direction flag -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte dirY = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ -+ // get direction flag for diagonal movement -+ final byte dirXY = getDirXY(dirX, dirY); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte direction; -+ -+ // load pointer coordinates -+ int gpx = gox; -+ int gpy = goy; -+ int gpz = goz; -+ -+ // load next pointer -+ int nx = gpx; -+ int ny = gpy; -+ -+ // loop -+ int count = 0; -+ while (count++ < Config.MAX_ITERATIONS) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -+ direction = 0; - -- if (hasGeoPos(prevX, prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -+ d -= dy; -+ d += dx; -+ nx += sx; -+ ny += sy; -+ direction |= dirXY; -+ } -+ else if (e2 > -dy) -+ { -+ d -= dy; -+ nx += sx; -+ direction |= dirX; -+ } -+ else if (e2 < dx) -+ { -+ d += dx; -+ ny += sy; -+ direction |= dirY; -+ } -+ -+ // obstacle found, return -+ if ((getNsweNearest(gpx, gpy, gpz) & direction) == 0) -+ { -+ return new GeoLocation(gpx, gpy, gpz); -+ } -+ -+ // update pointer coordinates -+ gpx = nx; -+ gpy = ny; -+ gpz = getHeightNearest(nx, ny, gpz); -+ -+ // target coordinates reached -+ if ((gpx == gtx) && (gpy == gty)) -+ { -+ if (gpz == gtz) - { -- return false; -+ // path found, Z coordinates are okay, return target point -+ return new GeoLocation(gtx, gty, gtz); - } -+ -+ // path found, Z coordinates are not okay, return last good point -+ return new GeoLocation(gpx, gpy, gpz); - } -+ } -+ -+ return new GeoLocation(gox, goy, goz); -+ } -+ -+ /** -+ * Returns diagonal NSWE flag format of combined two NSWE flags. -+ * @param dirX : X direction NSWE flag -+ * @param dirY : Y direction NSWE flag -+ * @return byte : NSWE flag of combined direction -+ */ -+ private static byte getDirXY(byte dirX, byte dirY) -+ { -+ // check axis directions -+ if (dirY == GeoStructure.CELL_FLAG_N) -+ { -+ if (dirX == GeoStructure.CELL_FLAG_W) -+ { -+ return GeoStructure.CELL_FLAG_NW; -+ } - -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return GeoStructure.CELL_FLAG_NE; - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) -+ if (dirX == GeoStructure.CELL_FLAG_W) - { -- // Different floors. -- return false; -+ return GeoStructure.CELL_FLAG_SW; - } - -- return true; -+ return GeoStructure.CELL_FLAG_SE; - } - -+ /** -+ * Returns the list of location objects as a result of complete path calculation. -+ * @param ox : origin x -+ * @param oy : origin y -+ * @param oz : origin z -+ * @param tx : target x -+ * @param ty : target y -+ * @param tz : target z -+ * @param instance -+ * @return {@code List} : complete path from nodes -+ */ -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ return null; -+ } -+ -+ /** -+ * Returns the instance of the {@link GeoEngine}. -+ * @return {@link GeoEngine} : The instance. -+ */ - public static GeoEngine getInstance() - { - return SingletonHolder.INSTANCE; -@@ -743,6 +934,6 @@ - - private static class SingletonHolder - { -- protected static final GeoEngine INSTANCE = new GeoEngine(); -+ protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); - } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (working copy) -@@ -20,88 +20,84 @@ - import java.util.LinkedList; - import java.util.List; - import java.util.ListIterator; --import java.util.logging.Level; --import java.util.logging.Logger; - - import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; --import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; --import org.l2jmobius.gameserver.model.World; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -+import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.instancezone.Instance; - - /** -- * @author -Nemesiss- -+ * @author Hasha - */ --public class GeoEnginePathfinding -+final class GeoEnginePathfinding extends GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); -+ // pre-allocated buffers -+ private final BufferHolder[] _buffers; - -- private BufferInfo[] _buffers; -- - protected GeoEnginePathfinding() - { -- try -+ super(); -+ -+ final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ _buffers = new BufferHolder[array.length]; -+ int count = 0; -+ for (int i = 0; i < array.length; i++) - { -- final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ final String buf = array[i]; -+ final String[] args = buf.split("x"); - -- _buffers = new BufferInfo[array.length]; -- -- String buf; -- String[] args; -- for (int i = 0; i < array.length; i++) -+ try - { -- buf = array[i]; -- args = buf.split("x"); -- if (args.length != 2) -- { -- throw new Exception("Invalid buffer definition: " + buf); -- } -- -- _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); -+ final int size = Integer.parseInt(args[1]); -+ count += size; -+ _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); - } -+ catch (Exception e) -+ { -+ LOGGER.warning("GeoEnginePathfinding: Can not load buffer setting: " + buf); -+ } - } -- catch (Exception e) -- { -- LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); -- throw new Error("CellPathFinding: load aborted"); -- } -+ -+ LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); - } - -- public boolean pathNodesExist(short regionoffset) -+ @Override -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- return false; -- } -- -- public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) -- { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -- if (!GeoEngine.getInstance().hasGeo(x, y)) -+ // get origin and check existing geo coords -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { - 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)) -+ -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coords -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { - 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)))); -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // Prepare buffer for pathfinding calculations -+ final NodeBuffer buffer = getBuffer(64 + (2 * Math.max(Math.abs(gox - gtx), Math.abs(goy - gty)))); - if (buffer == null) - { - return null; - } - -- List path = null; -+ // find path -+ List path = null; - try - { -- final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); -- -+ final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); - if (result == null) - { - return null; -@@ -125,33 +121,45 @@ - return path; - } - -- int currentX, currentY, currentZ; -- ListIterator middlePoint; -+ // get path list iterator -+ final ListIterator point = path.listIterator(); - -- middlePoint = path.listIterator(); -- currentX = x; -- currentY = y; -- currentZ = z; -+ // get node A (origin) -+ int nodeAx = gox; -+ int nodeAy = goy; -+ short nodeAz = goz; - -- while (middlePoint.hasNext()) -+ // get node B -+ GeoLocation nodeB = (GeoLocation) point.next(); -+ -+ // iterate thought the path to optimize it -+ int count = 0; -+ while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) - { -- final AbstractNodeLoc locMiddle = middlePoint.next(); -- if (!middlePoint.hasNext()) -- { -- break; -- } -+ // get node C -+ final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - -- final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); -- if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) -+ // check movement from node A to node C -+ final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); -+ if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) - { -- middlePoint.remove(); -+ // can move from node A to node C -+ -+ // remove node B -+ point.remove(); - } - else - { -- currentX = locMiddle.getX(); -- currentY = locMiddle.getY(); -- currentZ = locMiddle.getZ(); -+ // can not move from node A to node C -+ -+ // set node A (node B is part of path, update A coordinates) -+ nodeAx = nodeB.getGeoX(); -+ nodeAy = nodeB.getGeoY(); -+ nodeAz = (short) nodeB.getZ(); - } -+ -+ // set node B -+ nodeB = (GeoLocation) point.next(); - } - - return path; -@@ -158,144 +166,102 @@ - } - - /** -- * Convert geodata position to pathnode position -- * @param geo_pos -- * @return pathnode position -+ * Create list of node locations as result of calculated buffer node tree. -+ * @param node : the entry point -+ * @return List : list of node location - */ -- public short getNodePos(int geo_pos) -+ private static List constructPath(Node node) - { -- return (short) (geo_pos >> 3); // OK? -- } -- -- /** -- * Convert node position to pathnode block position -- * @param node_pos -- * @return pathnode block position (0...255) -- */ -- public short getNodeBlock(int node_pos) -- { -- return (short) (node_pos % 256); -- } -- -- public byte getRegionX(int node_pos) -- { -- return (byte) ((node_pos >> 8) + World.TILE_X_MIN); -- } -- -- public byte getRegionY(int node_pos) -- { -- return (byte) ((node_pos >> 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 node_x rx -- * @return -- */ -- public int calculateWorldX(short node_x) -- { -- return World.MAP_MIN_X + (node_x * 128) + 48; -- } -- -- /** -- * Convert pathnode y to World y position -- * @param node_y -- * @return -- */ -- public int calculateWorldY(short node_y) -- { -- return World.MAP_MIN_Y + (node_y * 128) + 48; -- } -- -- private List constructPath(AbstractNode nodeValue) -- { -- final LinkedList path = new LinkedList<>(); -- int previousDirectionX = Integer.MIN_VALUE; -- int previousDirectionY = Integer.MIN_VALUE; -- int directionX, directionY; -+ // create empty list -+ final LinkedList list = new LinkedList<>(); - -- AbstractNode node = nodeValue; -- while (node.getParent() != null) -+ // set direction X/Y -+ int dx = 0; -+ int dy = 0; -+ -+ // get target parent -+ Node target = node; -+ Node parent = target.getParent(); -+ -+ // while parent exists -+ int count = 0; -+ while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { -- directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); -- directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); -+ // get parent <> target direction X/Y -+ final int nx = parent.getLoc().getGeoX() - target.getLoc().getGeoX(); -+ final int ny = parent.getLoc().getGeoY() - target.getLoc().getGeoY(); - -- // only add a new route point if moving direction changes -- if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) -+ // direction has changed? -+ if ((dx != nx) || (dy != ny)) - { -- previousDirectionX = directionX; -- previousDirectionY = directionY; -+ // add node to the beginning of the list -+ list.addFirst(target.getLoc()); - -- path.addFirst(node.getLoc()); -- node.setLoc(null); -+ // update direction X/Y -+ dx = nx; -+ dy = ny; - } - -- node = node.getParent(); -+ // move to next node, set target and get its parent -+ target = parent; -+ parent = target.getParent(); - } - -- return path; -+ // return list -+ return list; - } - -- private CellNodeBuffer alloc(int size) -+ /** -+ * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer. -+ * @param size : pre-calculated minimal required size -+ * @return NodeBuffer : buffer -+ */ -+ private final NodeBuffer getBuffer(int size) - { -- CellNodeBuffer current = null; -- for (BufferInfo i : _buffers) -+ NodeBuffer current = null; -+ for (BufferHolder holder : _buffers) - { -- if (i.mapSize >= size) -+ // Find proper size of buffer -+ if (holder._size < size) - { -- for (CellNodeBuffer buf : i.buffer) -+ continue; -+ } -+ -+ // Find unlocked NodeBuffer -+ for (NodeBuffer buffer : holder._buffer) -+ { -+ if (!buffer.isLocked()) - { -- if (buf.lock()) -- { -- current = buf; -- break; -- } -+ continue; - } -- if (current != null) -- { -- break; -- } - -- // not found, allocate temporary buffer -- current = new CellNodeBuffer(i.mapSize); -- current.lock(); -- if (i.buffer.size() < i.count) -- { -- i.buffer.add(current); -- break; -- } -+ return buffer; - } -+ -+ // NodeBuffer not found, allocate temporary buffer -+ current = new NodeBuffer(holder._size); -+ current.isLocked(); - } - - return current; - } - -- private static final class BufferInfo -+ /** -+ * NodeBuffer container with specified size and count of separate buffers. -+ */ -+ private static final class BufferHolder - { -- final int mapSize; -- final int count; -- ArrayList buffer; -+ final int _size; -+ List _buffer; - -- public BufferInfo(int size, int cnt) -+ public BufferHolder(int size, int count) - { -- mapSize = size; -- count = cnt; -- buffer = new ArrayList<>(count); -+ _size = size; -+ _buffer = new ArrayList<>(count); -+ for (int i = 0; i < count; i++) -+ { -+ _buffer.add(new NodeBuffer(size)); -+ } - } - } -- -- public static GeoEnginePathfinding getInstance() -- { -- return SingletonHolder.INSTANCE; -- } -- -- private static class SingletonHolder -- { -- protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); -- } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (working copy) -@@ -0,0 +1,197 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @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 height of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getHeightNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first above given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, above given coordinates. -+ */ -+ public abstract short getHeightAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first below given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is closest to given coordinates. -+ * @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 the NSWE flag byte of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getNsweNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first above given coordinates. -+ * @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 getNsweAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first below given coordinates. -+ * @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 getNsweBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is closes layer to given coordinates. -+ * @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. -+ * @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 above given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is first layer below given coordinates. -+ * @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 index to data of the cell, which is first layer below given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexBelowOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeight(int index); -+ -+ /** -+ * Returns the height of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightOriginal(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNswe(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNsweOriginal(int index); -+ -+ /** -+ * Sets the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @param nswe : New NSWE flag byte. -+ */ -+ public abstract void setNswe(int index, byte nswe); -+ -+ /** -+ * Saves the block in L2D format to {@link BufferedOutputStream}. Used only for L2D geodata conversion. -+ * @param stream : The stream. -+ * @throws IOException : Can't save the block to steam. -+ */ -+ public abstract void saveBlock(BufferedOutputStream stream) throws IOException; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (working copy) -@@ -0,0 +1,252 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockComplex(ByteBuffer bb, GeoFormat format) -+ { -+ // initialize buffer -+ _buffer = new byte[GeoStructure.BLOCK_CELLS * 3]; -+ -+ // load data -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // 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); -+ } -+ else -+ { -+ // get nswe -+ final byte nswe = bb.get(); -+ _buffer[i * 3] = nswe; -+ -+ // get height -+ final short height = bb.getShort(); -+ _buffer[(i * 3) + 1] = (byte) (height & 0x00FF); -+ _buffer[(i * 3) + 2] = (byte) (height >> 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height > worldZ ? height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height < worldZ ? height : Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public 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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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 int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_COMPLEX_L2D); -+ -+ // write block data -+ stream.write(_buffer, 0, GeoStructure.BLOCK_CELLS * 3); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (working copy) -@@ -0,0 +1,176 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockFlat extends ABlock -+{ -+ protected final short _height; -+ protected byte _nswe; -+ -+ /** -+ * Creates FlatBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockFlat(ByteBuffer bb, GeoFormat format) -+ { -+ _height = bb.getShort(); -+ _nswe = format != GeoFormat.L2D ? 0x0F : (byte) (0xFF); -+ if (format == GeoFormat.L2OFF) -+ { -+ bb.getShort(); -+ } -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return true; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height > worldZ ? _height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height < worldZ ? _height : Short.MAX_VALUE; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height > worldZ ? _nswe : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height < worldZ ? _nswe : 0; -+ } -+ -+ @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return index -+ return _height < worldZ ? 0 : -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _nswe = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_FLAT_L2D); -+ -+ // write height -+ stream.write((byte) (_height & 0x00FF)); -+ stream.write((byte) (_height >> 8)); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (working copy) -@@ -0,0 +1,465 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+import java.nio.ByteOrder; -+import java.util.Arrays; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockMultilayer extends ABlock -+{ -+ private static final int MAX_LAYERS = Byte.MAX_VALUE; -+ -+ private static ByteBuffer _temp; -+ -+ /** -+ * Initializes the temporarily buffer. -+ */ -+ public static void initialize() -+ { -+ // initialize temporarily buffer and sorting mechanism -+ _temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3); -+ _temp.order(ByteOrder.LITTLE_ENDIAN); -+ } -+ -+ /** -+ * Releases temporarily buffer. -+ */ -+ public static void release() -+ { -+ _temp = null; -+ } -+ -+ protected byte[] _buffer; -+ -+ /** -+ * Implicit constructor for children class. -+ */ -+ protected BlockMultilayer() -+ { -+ _buffer = null; -+ } -+ -+ /** -+ * Creates MultilayerBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockMultilayer(ByteBuffer bb, GeoFormat format) -+ { -+ // 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 = format != GeoFormat.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++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // get data -+ final short data = bb.getShort(); -+ -+ // add nswe and height -+ _temp.put((byte) (data & 0x000F)); -+ _temp.putShort((short) ((short) (data & 0xFFF0) >> 1)); -+ } -+ else -+ { -+ // add nswe -+ _temp.put(bb.get()); -+ -+ // add height -+ _temp.putShort(bb.getShort()); -+ } -+ } -+ } -+ -+ // 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 height -+ if (height > worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, return minimum value -+ return Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 height -+ if (height < worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, return maximum value -+ return Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 nswe -+ if (height > worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 nswe -+ if (height < worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @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 first layer data (first from bottom) -+ byte layers = _buffer[index++]; -+ -+ // loop though all cell layers, find closest layer -+ 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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ // get height -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(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]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ // get nswe -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ // set nswe -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_MULTILAYER_L2D); -+ -+ // for each cell -+ int index = 0; -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ // write layers count -+ final byte layers = _buffer[index++]; -+ stream.write(layers); -+ -+ // write cell data -+ stream.write(_buffer, index, layers * 3); -+ -+ // move index to next cell -+ index += layers * 3; -+ } -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (working copy) -@@ -0,0 +1,150 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockNull extends ABlock -+{ -+ private final byte _nswe; -+ -+ public BlockNull() -+ { -+ _nswe = (byte) 0xFF; -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return false; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweBelow(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) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) -+ { -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (nonexistent) -@@ -1,48 +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 . -- */ --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() -- { -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (nonexistent) -@@ -1,77 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (nonexistent) -@@ -1,56 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (working copy) -@@ -0,0 +1,39 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public enum GeoFormat -+{ -+ L2J("%d_%d.l2j"), -+ L2OFF("%d_%d_conv.dat"), -+ L2D("%d_%d.l2d"); -+ -+ private final String _filename; -+ -+ private GeoFormat(String filename) -+ { -+ _filename = filename; -+ } -+ -+ public String getFilename() -+ { -+ return _filename; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (working copy) -@@ -0,0 +1,67 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.geoengine.GeoEngine; -+import org.l2jmobius.gameserver.model.Location; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoLocation extends Location -+{ -+ private byte _nswe; -+ -+ public GeoLocation(int x, int y, int z) -+ { -+ super(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public void set(int x, int y, short z) -+ { -+ super.setXYZ(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public int getGeoX() -+ { -+ return _x; -+ } -+ -+ public int getGeoY() -+ { -+ return _y; -+ } -+ -+ @Override -+ public int getX() -+ { -+ return GeoEngine.getWorldX(_x); -+ } -+ -+ @Override -+ public int getY() -+ { -+ return GeoEngine.getWorldY(_y); -+ } -+ -+ public byte getNSWE() -+ { -+ return _nswe; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (working copy) -@@ -0,0 +1,70 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoStructure -+{ -+ // cells -+ public static final byte CELL_FLAG_E = 1 << 0; -+ public static final byte CELL_FLAG_W = 1 << 1; -+ public static final byte CELL_FLAG_S = 1 << 2; -+ public static final byte CELL_FLAG_N = 1 << 3; -+ public static final byte CELL_FLAG_SE = 1 << 4; -+ public static final byte CELL_FLAG_SW = 1 << 5; -+ public static final byte CELL_FLAG_NE = 1 << 6; -+ public static final byte CELL_FLAG_NW = (byte) (1 << 7); -+ -+ public static final int CELL_HEIGHT = 8; -+ public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6; -+ -+ // blocks -+ public static final byte TYPE_FLAT_L2J_L2OFF = 0; -+ public static final byte TYPE_FLAT_L2D = (byte) 0xD0; -+ public static final byte TYPE_COMPLEX_L2J = 1; -+ public static final byte TYPE_COMPLEX_L2OFF = 0x40; -+ public static final byte TYPE_COMPLEX_L2D = (byte) 0xD1; -+ 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) -+ public static final byte TYPE_MULTILAYER_L2D = (byte) 0xD2; -+ -+ 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; -+ -+ // regions -+ 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; -+ -+ // global geodata -+ private static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); -+ private 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; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (nonexistent) -@@ -1,42 +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 . -- */ --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); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (working copy) -@@ -0,0 +1,53 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public interface IGeoObject -+{ -+ /** -+ * Returns geodata X coordinate of the {@link IGeoObject}. -+ * @return int : Geodata X coordinate. -+ */ -+ int getGeoX(); -+ -+ /** -+ * Returns geodata Y coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Y coordinate. -+ */ -+ int getGeoY(); -+ -+ /** -+ * Returns geodata Z coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Z coordinate. -+ */ -+ int getGeoZ(); -+ -+ /** -+ * Returns height of the {@link IGeoObject}. -+ * @return int : Height. -+ */ -+ int getHeight(); -+ -+ /** -+ * Returns {@link IGeoObject} data. -+ * @return byte[][] : {@link IGeoObject} data. -+ */ -+ byte[][] getObjectGeoData(); -+} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (nonexistent) -@@ -1,47 +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 . -- */ --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(); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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); -- -- // 1 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (nonexistent) -@@ -1,55 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --/** -- * @author HorridoJoho -- */ --public final class NullRegion implements IRegion --{ -- public static final NullRegion INSTANCE = new NullRegion(); -- -- @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 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Region.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (nonexistent) -@@ -1,92 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (nonexistent) -@@ -1,87 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public abstract class AbstractNode --{ -- private T _loc; -- private AbstractNode _parent; -- -- public AbstractNode(T loc) -- { -- _loc = loc; -- } -- -- public void setParent(AbstractNode p) -- { -- _parent = p; -- } -- -- public AbstractNode getParent() -- { -- return _parent; -- } -- -- public T getLoc() -- { -- return _loc; -- } -- -- public void setLoc(T l) -- { -- _loc = l; -- } -- -- @Override -- public int hashCode() -- { -- final int prime = 31; -- int result = 1; -- result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); -- return result; -- } -- -- @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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (nonexistent) -@@ -1,33 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --/** -- * @author -Nemesiss- -- */ --public abstract class AbstractNodeLoc --{ -- public abstract int getX(); -- -- public abstract int getY(); -- -- public abstract int getZ(); -- -- public abstract int getNodeX(); -- -- public abstract int getNodeY(); --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (nonexistent) -@@ -1,67 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public class CellNode extends AbstractNode --{ -- 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (nonexistent) -@@ -1,348 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --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 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) -- { -- _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(); -- } -- -- public final List debugPath() -- { -- final List 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 void getNeighbors() -- { -- if (!_current.getLoc().canGoAll()) -- { -- 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); -- } -- -- // SouthEast -- if ((nodeE != null) && (nodeS != null)) -- { -- if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) -- { -- addNode(x + 1, y + 1, z, true); -- } -- } -- -- // SouthWest -- if ((nodeS != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) -- { -- addNode(x - 1, y + 1, z, true); -- } -- } -- -- // NorthEast -- if ((nodeN != null) && (nodeE != null)) -- { -- if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) -- { -- addNode(x + 1, y - 1, z, true); -- } -- } -- -- // NorthWest -- if ((nodeN != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) -- { -- addNode(x - 1, y - 1, z, true); -- } -- } -- } -- -- private 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(); -- // reinit node if needed -- if (result.getLoc() != null) -- { -- result.getLoc().set(x, y, z); -- } -- else -- { -- result.setLoc(new NodeLoc(x, y, z)); -- } -- } -- -- return result; -- } -- -- private 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 boolean isHighWeight(int x, int y, int z) -- { -- final CellNode result = getNode(x, y, z); -- if (result == null) -- { -- return true; -- } -- -- if (!result.getLoc().canGoAll()) -- { -- return true; -- } -- if (Math.abs(result.getLoc().getZ() - z) > 16) -- { -- return true; -- } -- -- return false; -- } -- -- private 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (working copy) -@@ -0,0 +1,87 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+ -+/** -+ * @author Hasha -+ */ -+public class Node -+{ -+ // node coords and nswe flag -+ private GeoLocation _loc; -+ -+ // node parent (for reverse path construction) -+ private Node _parent; -+ // node child (for moving over nodes during iteration) -+ private Node _child; -+ -+ // node G cost (movement cost = parent movement cost + current movement cost) -+ private double _cost = -1000; -+ -+ public void setLoc(int x, int y, int z) -+ { -+ _loc = new GeoLocation(x, y, z); -+ } -+ -+ public GeoLocation getLoc() -+ { -+ return _loc; -+ } -+ -+ public void setParent(Node parent) -+ { -+ _parent = parent; -+ } -+ -+ public Node getParent() -+ { -+ return _parent; -+ } -+ -+ public void setChild(Node child) -+ { -+ _child = child; -+ } -+ -+ public Node getChild() -+ { -+ return _child; -+ } -+ -+ public void setCost(double cost) -+ { -+ _cost = cost; -+ } -+ -+ public double getCost() -+ { -+ return _cost; -+ } -+ -+ public void free() -+ { -+ // reset node location -+ _loc = null; -+ -+ // reset node parent, child and cost -+ _parent = null; -+ _child = null; -+ _cost = -1000; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (working copy) -@@ -0,0 +1,306 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import java.util.concurrent.locks.ReentrantLock; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+ -+/** -+ * @author DS, Hasha; Credits to Diamond -+ */ -+public class NodeBuffer -+{ -+ private final ReentrantLock _lock = new ReentrantLock(); -+ private final int _size; -+ private final Node[][] _buffer; -+ -+ // center coordinates -+ private int _cx = 0; -+ private int _cy = 0; -+ -+ // target coordinates -+ private int _gtx = 0; -+ private int _gty = 0; -+ private short _gtz = 0; -+ -+ private Node _current = null; -+ -+ /** -+ * Constructor of NodeBuffer. -+ * @param size : one dimension size of buffer -+ */ -+ public NodeBuffer(int size) -+ { -+ // set size -+ _size = size; -+ -+ // initialize buffer -+ _buffer = new Node[size][size]; -+ for (int x = 0; x < size; x++) -+ { -+ for (int y = 0; y < size; y++) -+ { -+ _buffer[x][y] = 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 Node : first node of path -+ */ -+ public Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) -+ { -+ // set coordinates (middle of the line (gox,goy) - (gtx,gty), will be in the center of the buffer) -+ _cx = gox + ((gtx - gox - _size) / 2); -+ _cy = goy + ((gty - goy - _size) / 2); -+ -+ _gtx = gtx; -+ _gty = gty; -+ _gtz = gtz; -+ -+ _current = getNode(gox, goy, goz); -+ _current.setCost(getCostH(gox, goy, goz)); -+ -+ int count = 0; -+ do -+ { -+ // reached target? -+ if ((_current.getLoc().getGeoX() == _gtx) && (_current.getLoc().getGeoY() == _gty) && (Math.abs(_current.getLoc().getZ() - _gtz) < 8)) -+ { -+ return _current; -+ } -+ -+ // expand current node -+ expand(); -+ -+ // move pointer -+ _current = _current.getChild(); -+ } -+ while ((_current != null) && (count++ < Config.MAX_ITERATIONS)); -+ -+ return null; -+ } -+ -+ public boolean isLocked() -+ { -+ return _lock.tryLock(); -+ } -+ -+ public void free() -+ { -+ _current = null; -+ -+ for (Node[] nodes : _buffer) -+ { -+ for (Node node : nodes) -+ { -+ if (node.getLoc() != null) -+ { -+ node.free(); -+ } -+ } -+ } -+ -+ _lock.unlock(); -+ } -+ -+ /** -+ * Check _current Node and add its neighbors to the buffer. -+ */ -+ private final void expand() -+ { -+ // can't move anywhere, don't expand -+ final byte nswe = _current.getLoc().getNSWE(); -+ if (nswe == 0) -+ { -+ return; -+ } -+ -+ // get geo coords of the node to be expanded -+ final int x = _current.getLoc().getGeoX(); -+ final int y = _current.getLoc().getGeoY(); -+ final short z = (short) _current.getLoc().getZ(); -+ -+ // can move north, expand -+ if ((nswe & GeoStructure.CELL_FLAG_N) != 0) -+ { -+ addNode(x, y - 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move south, expand -+ if ((nswe & GeoStructure.CELL_FLAG_S) != 0) -+ { -+ addNode(x, y + 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_W) != 0) -+ { -+ addNode(x - 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_E) != 0) -+ { -+ addNode(x + 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move north-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NW) != 0) -+ { -+ addNode(x - 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move north-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NE) != 0) -+ { -+ addNode(x + 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SW) != 0) -+ { -+ addNode(x - 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SE) != 0) -+ { -+ addNode(x + 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ } -+ -+ /** -+ * Returns node, if it exists in buffer. -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param z : node Z coord -+ * @return Node : node, if exits in buffer -+ */ -+ private final Node getNode(int x, int y, short z) -+ { -+ // check node X out of coordinates -+ final int ix = x - _cx; -+ if ((ix < 0) || (ix >= _size)) -+ { -+ return null; -+ } -+ -+ // check node Y out of coordinates -+ final int iy = y - _cy; -+ if ((iy < 0) || (iy >= _size)) -+ { -+ return null; -+ } -+ -+ // get node -+ final Node result = _buffer[ix][iy]; -+ -+ // check and update -+ if (result.getLoc() == null) -+ { -+ result.setLoc(x, y, z); -+ } -+ -+ // return node -+ return result; -+ } -+ -+ /** -+ * Add node given by coordinates to the buffer. -+ * @param x : geo X coord -+ * @param y : geo Y coord -+ * @param z : geo Z coord -+ * @param weight : weight of movement to new node -+ */ -+ private final void addNode(int x, int y, short z, int weight) -+ { -+ // get node to be expanded -+ final Node node = getNode(x, y, z); -+ if (node == null) -+ { -+ return; -+ } -+ -+ // Z distance between nearby cells is higher than cell size -+ if (node.getLoc().getZ() > (z + (2 * GeoStructure.CELL_HEIGHT))) -+ { -+ return; -+ } -+ -+ // node was already expanded, return -+ if (node.getCost() >= 0) -+ { -+ return; -+ } -+ -+ node.setParent(_current); -+ if (node.getLoc().getNSWE() != (byte) 0xFF) -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + (weight * Config.OBSTACLE_MULTIPLIER)); -+ } -+ else -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + weight); -+ } -+ -+ Node current = _current; -+ int count = 0; -+ while ((current.getChild() != null) && (count < (Config.MAX_ITERATIONS * 4))) -+ { -+ count++; -+ if (current.getChild().getCost() > node.getCost()) -+ { -+ node.setChild(current.getChild()); -+ break; -+ } -+ current = current.getChild(); -+ } -+ -+ if (count >= (Config.MAX_ITERATIONS * 4)) -+ { -+ System.err.println("Pathfinding: too long loop detected, cost:" + node.getCost()); -+ } -+ -+ current.setChild(node); -+ } -+ -+ /** -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param i : node Z coord -+ * @return double : node cost -+ */ -+ private final double getCostH(int x, int y, int i) -+ { -+ final int dX = x - _gtx; -+ final int dY = y - _gty; -+ final int dZ = (i - _gtz) / GeoStructure.CELL_HEIGHT; -+ -+ // return (Math.abs(dX) + Math.abs(dY) + Math.abs(dZ)) * Config.HEURISTIC_WEIGHT; // Manhattan distance -+ return Math.sqrt((dX * dX) + (dY * dY) + (dZ * dZ)) * Config.HEURISTIC_WEIGHT; // Direct distance -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -- --/** -- * @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 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 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; -- // return super.hashCode(); -- } -- -- @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; -- if (_x != other._x) -- { -- return false; -- } -- if (_y != other._y) -- { -- return false; -- } -- if (_goNorth != other._goNorth) -- { -- return false; -- } -- if (_goEast != other._goEast) -- { -- return false; -- } -- if (_goSouth != other._goSouth) -- { -- return false; -- } -- if (_goWest != other._goWest) -- { -- return false; -- } -- if (_geoHeight != other._geoHeight) -- { -- return false; -- } -- return true; -- } --} -Index: java/org/l2jmobius/gameserver/model/actor/Creature.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/Creature.java (revision 8428) -+++ java/org/l2jmobius/gameserver/model/actor/Creature.java (working copy) -@@ -65,8 +65,6 @@ - import org.l2jmobius.gameserver.enums.TeleportWhereType; - import org.l2jmobius.gameserver.enums.UserInfoType; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; - import org.l2jmobius.gameserver.instancemanager.IdManager; - import org.l2jmobius.gameserver.instancemanager.MapRegionManager; - import org.l2jmobius.gameserver.instancemanager.QuestManager; -@@ -2566,7 +2564,7 @@ - - public boolean disregardingGeodata; - public int onGeodataPathIndex; -- public List geoPath; -+ public List geoPath; - public int geoPathAccurateTx; - public int geoPathAccurateTy; - public int geoPathGtx; -@@ -3443,7 +3441,7 @@ - if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) - { - // Path calculation -- overrides previous movement check -- m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); -+ m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); - if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found - { - if (isPlayer() && !_isFlying && !isInWater) -Index: java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (revision 8424) -+++ java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (working copy) -@@ -12746,7 +12746,7 @@ - { - if (Config.CORRECT_PLAYER_Z) - { -- final int nearestZ = GeoEngine.getInstance().getHigherHeight(getX(), getY(), getZ()); -+ final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); - if (getZ() < nearestZ) - { - teleToLocation(new Location(getX(), getY(), nearestZ)); -Index: java/org/l2jmobius/gameserver/util/GeoUtils.java -=================================================================== ---- java/org/l2jmobius/gameserver/util/GeoUtils.java (revision 8409) -+++ java/org/l2jmobius/gameserver/util/GeoUtils.java (working copy) -@@ -19,7 +19,7 @@ - import java.awt.Color; - - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; - import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; - -@@ -26,25 +26,25 @@ - /** - * @author HorridoJoho - */ --public final class GeoUtils -+public class GeoUtils - { - public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); - - final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); - - while (iter.next()) - { -- final int wx = GeoEngine.getInstance().getWorldX(iter.x()); -- final int wy = GeoEngine.getInstance().getWorldY(iter.y()); -+ final int wx = GeoEngine.getWorldX(iter.x()); -+ final int wy = GeoEngine.getWorldY(iter.y()); - - prim.addPoint(Color.RED, wx, wy, z); - } -@@ -53,21 +53,21 @@ - - public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); -- int wy = GeoEngine.getInstance().getWorldY(prevY); -+ int wx = GeoEngine.getWorldX(prevX); -+ int wy = GeoEngine.getWorldY(prevY); - int wz = iter.z(); - prim.addPoint(Color.RED, wx, wy, wz); - -@@ -78,8 +78,8 @@ - - if ((curX != prevX) || (curY != prevY)) - { -- wx = GeoEngine.getInstance().getWorldX(curX); -- wy = GeoEngine.getInstance().getWorldY(curY); -+ wx = GeoEngine.getWorldX(curX); -+ wy = GeoEngine.getWorldY(curY); - wz = iter.z(); - - prim.addPoint(Color.RED, wx, wy, wz); -@@ -93,7 +93,7 @@ - - private static Color getDirectionColor(int x, int y, int z, int nswe) - { -- if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) -+ if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) - { - return Color.GREEN; - } -@@ -109,9 +109,8 @@ - int iPacket = 0; - - ExServerPrimitive exsp = null; -- final GeoEngine ge = GeoEngine.getInstance(); -- final int playerGx = ge.getGeoX(player.getX()); -- final int playerGy = ge.getGeoY(player.getY()); -+ final int playerGx = GeoEngine.getGeoX(player.getX()); -+ final int playerGy = GeoEngine.getGeoY(player.getY()); - for (int dx = -geoRadius; dx <= geoRadius; ++dx) - { - for (int dy = -geoRadius; dy <= geoRadius; ++dy) -@@ -135,12 +134,12 @@ - final int gx = playerGx + dx; - final int gy = playerGy + dy; - -- final int x = ge.getWorldX(gx); -- final int y = ge.getWorldY(gy); -- final int z = ge.getNearestZ(gx, gy, player.getZ()); -+ final int x = GeoEngine.getWorldX(gx); -+ final int y = GeoEngine.getWorldY(gy); -+ final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); - - // north arrow -- Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); -+ Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); - 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); -@@ -147,7 +146,7 @@ - exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z); - - // east arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); - 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); -@@ -154,13 +153,13 @@ - exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z); - - // south arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); - 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, Cell.NSWE_WEST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); - 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); -@@ -188,15 +187,15 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_EAST; -+ return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_EAST; -+ return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; - } - else - { -- return Cell.NSWE_EAST; -+ return GeoStructure.CELL_FLAG_E; // Direction.EAST; - } - } - else if (x < lastX) // west -@@ -203,26 +202,27 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_WEST; -+ return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_WEST; -+ return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; - } - else - { -- return Cell.NSWE_WEST; -+ return GeoStructure.CELL_FLAG_W; // Direction.WEST; - } - } -- else // unchanged x -+ else -+ // unchanged x - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH; -+ return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH; -+ return GeoStructure.CELL_FLAG_N; // Direction.NORTH; - } - else - { -Index: java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java -=================================================================== ---- java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (nonexistent) -+++ java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (working copy) -@@ -0,0 +1,386 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.tools.geodataconverter; -+ -+import java.io.BufferedOutputStream; -+import java.io.File; -+import java.io.FileOutputStream; -+import java.io.RandomAccessFile; -+import java.nio.ByteOrder; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.Scanner; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.commons.util.PropertiesParser; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoDataConverter -+{ -+ private static GeoFormat _format; -+ private static ABlock[][] _blocks; -+ -+ public static void main(String[] args) -+ { -+ // initialize config -+ loadGeoengineConfigs(); -+ -+ // get geodata format -+ String type = ""; -+ try (Scanner scn = new Scanner(System.in)) -+ { -+ while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) -+ { -+ System.out.println("GeoDataConverter: Select source geodata type:"); -+ System.out.println(" J: L2J (e.g. 23_22.l2j)"); -+ System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); -+ System.out.println(" E: Exit"); -+ System.out.print("Choice: "); -+ type = scn.next(); -+ } -+ } -+ if (type.equalsIgnoreCase("E")) -+ { -+ System.exit(0); -+ } -+ -+ _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; -+ -+ // start conversion -+ System.out.println("GeoDataConverter: Converting all " + _format + " files."); -+ -+ // initialize geodata container -+ _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files -+ int converted = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) -+ { -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) -+ { -+ final String input = String.format(_format.getFilename(), rx, ry); -+ final String filepath = Config.GEODATA_PATH; -+ final File f = new File(filepath + input); -+ if (f.exists() && !f.isDirectory()) -+ { -+ // load geodata -+ if (!loadGeoBlocks(input)) -+ { -+ System.out.println("GeoDataConverter: Unable to load " + input + " region file."); -+ continue; -+ } -+ -+ // recalculate nswe -+ if (!recalculateNswe()) -+ { -+ System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); -+ continue; -+ } -+ -+ // save geodata -+ final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); -+ if (!saveGeoBlocks(output)) -+ { -+ System.out.println("GeoDataConverter: Unable to save " + output + " region file."); -+ continue; -+ } -+ -+ converted++; -+ System.out.println("GeoDataConverter: Created " + output + " region file."); -+ } -+ } -+ } -+ System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); -+ } -+ -+ /** -+ * Loads geo blocks from buffer of the region file. -+ * @param filename : The name of the to load. -+ * @return boolean : True when successful. -+ */ -+ private static boolean loadGeoBlocks(String filename) -+ { -+ // region file is load-able, try to load it -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) -+ { -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) -+ if (_format == GeoFormat.L2OFF) -+ { -+ for (int i = 0; i < 18; i++) -+ { -+ buffer.get(); -+ } -+ } -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ if (_format == GeoFormat.L2J) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2J: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2J: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ else -+ { -+ // get block type -+ final short type = buffer.getShort(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ default: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ if (buffer.remaining() > 0) -+ { -+ System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ return false; -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); -+ return false; -+ } -+ } -+ -+ /** -+ * Recalculate diagonal flags for the region file. -+ * @return boolean : True when successful. -+ */ -+ private static boolean recalculateNswe() -+ { -+ try -+ { -+ for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) -+ { -+ for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) -+ { -+ // get block -+ final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // skip flat blocks -+ if (block instanceof BlockFlat) -+ { -+ continue; -+ } -+ -+ // for complex and multilayer blocks go though all layers -+ short height = Short.MAX_VALUE; -+ int index; -+ while ((index = block.getIndexBelow(x, y, height)) != -1) -+ { -+ // get height and nswe -+ height = block.getHeight(index); -+ byte nswe = block.getNswe(index); -+ -+ // update nswe with diagonal flags -+ nswe = updateNsweBelow(x, y, height, nswe); -+ -+ // set nswe of the cell -+ block.setNswe(index, nswe); -+ } -+ } -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ /** -+ * Updates the NSWE flag with diagonal flags. -+ * @param x : Geodata X coordinate. -+ * @param y : Geodata Y coordinate. -+ * @param z : Geodata Z coordinate. -+ * @param nsweValue : NSWE flag to be updated. -+ * @return byte : Updated NSWE flag. -+ */ -+ private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) -+ { -+ byte nswe = nsweValue; -+ -+ // calculate virtual layer height -+ final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); -+ -+ // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) -+ final byte nsweN = getNsweBelow(x, y - 1, height); -+ final byte nsweS = getNsweBelow(x, y + 1, height); -+ final byte nsweW = getNsweBelow(x - 1, y, height); -+ final byte nsweE = getNsweBelow(x + 1, y, height); -+ -+ // north-west -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NW; -+ } -+ -+ // north-east -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NE; -+ } -+ -+ // south-west -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SW; -+ } -+ -+ // south-east -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SE; -+ } -+ -+ return nswe; -+ } -+ -+ private static byte getNsweBelow(int geoX, int geoY, short worldZ) -+ { -+ // out of geo coordinates -+ if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) -+ { -+ return 0; -+ } -+ -+ // out of geo coordinates -+ if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) -+ { -+ return 0; -+ } -+ -+ // get block -+ final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // get index, when valid, return nswe -+ final int index = block.getIndexBelow(geoX, geoY, worldZ); -+ return index == -1 ? 0 : block.getNswe(index); -+ } -+ -+ /** -+ * Save region file to file. -+ * @param filename : The name of file to save. -+ * @return boolean : True when successful. -+ */ -+ private static boolean saveGeoBlocks(String filename) -+ { -+ try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) -+ { -+ // loop over region blocks and save each block -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[ix][iy].saveBlock(bos); -+ } -+ } -+ -+ // flush data to file -+ bos.flush(); -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ private static void loadGeoengineConfigs() -+ { -+ final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); -+ Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); -+ Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); -+ Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); -+ Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); -+ Config.PATHFINDING = geoData.getBoolean("PathFinding", true); -+ Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -+ Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); -+ Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); -+ Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); -+ Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); -+ Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); -+ } -+} -\ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/geodata/Readme.txt b/L2J_Mobius_1.0_Ertheia/dist/game/data/geodata/Readme.txt index a480c8c380..2611882e56 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/geodata/Readme.txt +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/geodata/Readme.txt @@ -23,8 +23,8 @@ a - Prerequisites b - Make it work ---------------------------------------------- -To make geodata working: -* unpack your geodata files into "/data/geodata" folder -* open "/config/GeoEngine.ini" with your favorite text editor and then edit following config: - - CoordSynchronize = 2 -* If you do not use any geodata files, the server will automatically change this setting to -1. +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. diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java index 037b29d579..89425b0d85 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java @@ -295,7 +295,7 @@ public class FourSepulchers extends AbstractNpcAI implements IXmlReader { if ((npc != null) && !npc.isDead()) { - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), npc.getSpawn().getLocation().getX() + getRandom(-400, 400), npc.getSpawn().getLocation().getY() + getRandom(-400, 400), npc.getZ(), npc.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), npc.getSpawn().getLocation().getX() + getRandom(-400, 400), npc.getSpawn().getLocation().getY() + getRandom(-400, 400), npc.getZ(), npc.getInstanceWorld()); if (Util.calculateDistance(npc, npc.getSpawn().getLocation(), false, false) < 600) { npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java index 7aa8789df0..00bffa39b8 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java @@ -270,7 +270,7 @@ public class PrimevalIsle extends AbstractNpcAI final double cos = Math.cos(radian); final int newX = (int) (npc.getX() + (cos * distance)); final int newY = (int) (npc.getY() + (sin * distance)); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, npc.getZ(), npc.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, npc.getZ(), npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, loc, 0); } else if (ag_type == 1) diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java index 0a77f7404d..772f5fb5d7 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java @@ -78,7 +78,7 @@ public class BoyAndGirl extends AbstractNpcAI startQuestTimer("NPC_SHOUT", 10000 + (getRandom(5) * 1000), npc, null); npc.setRunning(); final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 200, 600); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); return super.onSpawn(npc); } @@ -86,7 +86,7 @@ public class BoyAndGirl extends AbstractNpcAI public void onMoveFinished(Npc npc) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 200, 600); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); super.onMoveFinished(npc); } diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java index 558f2631e6..3bcf3810cb 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java @@ -48,7 +48,7 @@ public class Devno extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java index 7525cc99e8..ce81f5c05c 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java @@ -48,7 +48,7 @@ public class Eleve extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java index 7d9150b156..f4e942c326 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java @@ -46,7 +46,7 @@ public class Handermonkey extends AbstractNpcAI { final int x = npc.getSpawn().getX() + (getRandom(-100, 100)); final int y = npc.getSpawn().getY() + (getRandom(-100, 100)); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x, y, npc.getZ(), npc.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x, y, npc.getZ(), npc.getInstanceWorld()); addMoveToDesire(npc, loc, 0); } else diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java index c993ce8936..7df61da041 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java @@ -48,7 +48,7 @@ public class Karonf extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java index 6e8cbee920..589d376f6b 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java @@ -48,7 +48,7 @@ public class Marsha extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java index c616abccc6..5c09d531c8 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java @@ -48,7 +48,7 @@ public class Morgan extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java index 3652b4ab02..fe20952121 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java @@ -48,7 +48,7 @@ public class Rubentis extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", 10000 + (getRandom(5) * 1000), npc, null); } diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java index beece64521..b0944f763d 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java @@ -63,7 +63,7 @@ public class Vortex extends AbstractNpcAI final int x = (int) (attackers.getX() + (600 * Math.cos(radians))); final int y = (int) (attackers.getY() + (600 * Math.sin(radians))); final int z = attackers.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(attackers.getX(), attackers.getY(), attackers.getZ(), x, y, z, attackers.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(attackers.getX(), attackers.getY(), attackers.getZ(), x, y, z, attackers.getInstanceWorld()); attackers.broadcastPacket(new FlyToLocation(attackers, x, y, z, FlyToLocation.FlyType.THROW_UP, 800, 800, 800)); attackers.setXYZ(loc); attackers.broadcastPacket(new ValidateLocation(attackers)); diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/others/FleeMonsters.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/others/FleeMonsters.java index 9346aebc95..b5de804b28 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/others/FleeMonsters.java +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/others/FleeMonsters.java @@ -68,7 +68,7 @@ public class FleeMonsters extends AbstractNpcAI final int posX = (int) (npc.getX() + (FLEE_DISTANCE * Math.cos(radians))); final int posY = (int) (npc.getY() + (FLEE_DISTANCE * Math.sin(radians))); final int posZ = npc.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, attacker.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); return super.onAttack(npc, attacker, damage, isSummon); } diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index 524652cbc1..18511c6541 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { @@ -133,8 +133,8 @@ public class AdminGeodata implements IAdminCommandHandler } case "admin_geomap": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y + " (" + ((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + "," + ((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + " to " + ((((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + "," + ((((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + ")"); break; } diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java index f3dffa1227..76572f7f25 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java @@ -57,8 +57,8 @@ public class AdminMissingHtmls implements IAdminCommandHandler { case "admin_geomap_missing_htmls": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final int topLeftX = (x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE; final int topLeftY = (y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE; final int bottomRightX = (((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1; diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index fdb2319138..a0a34f1051 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -19,9 +19,9 @@ package handlers.admincommandhandlers; import java.util.List; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; +import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.util.BuilderUtil; @@ -44,13 +44,13 @@ public class AdminPathNode implements IAdminCommandHandler } if (activeChar.getTarget() != null) { - final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { BuilderUtil.sendSysMessage(activeChar, "No Route!"); return true; } - for (AbstractNodeLoc a : path) + for (Location a : path) { BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/Blink.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/Blink.java index 22f2c84eae..22697e8e20 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/Blink.java +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/Blink.java @@ -88,7 +88,7 @@ public class Blink extends AbstractEffect final int y = effected.getY() + y1; final int z = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, destination, _flyType, _flySpeed, _flyDelay, _animationSpeed)); diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/Fear.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/Fear.java index 95a5afdf98..88c795e04b 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/Fear.java +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/Fear.java @@ -100,7 +100,7 @@ public class Fear extends AbstractEffect final int posY = (int) (effected.getY() + (FEAR_RANGE * Math.sin(radians))); final int posZ = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); } } diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java index fddd94c60a..55c2e78d8a 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java @@ -57,7 +57,7 @@ public class FlyAway extends AbstractEffect final int y = (int) (effector.getY() - (nRadius * (dy / distance))); final int z = effector.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.broadcastPacket(new FlyToLocation(effected, destination, FlyType.THROW_UP)); effected.setXYZ(destination); diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java index 3c3f9aca62..14809ac499 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java @@ -179,7 +179,7 @@ public class KnockBack extends AbstractEffect final int x = (int) (effected.getX() + (_distance * Math.cos(radians))); final int y = (int) (effected.getY() + (_distance * Math.sin(radians))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, loc, _type, _speed, _delay, _animationSpeed)); diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/PullBack.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/PullBack.java index ed6a37eff6..38fe2fe9d0 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/PullBack.java +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/PullBack.java @@ -73,7 +73,7 @@ public class PullBack extends AbstractEffect } // In retail, you get debuff, but you are not even moved if there is obstacle. You are still disabled from using skills and moving though. - if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effector.getInstanceWorld())) + if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effected.getInstanceWorld())) { effected.broadcastPacket(new FlyToLocation(effected, effector, _type, _speed, _delay, _animationSpeed)); effected.setXYZ(effector.getX(), effector.getY(), GeoEngine.getInstance().getHeight(effector.getX(), effector.getY(), effector.getZ()) + 10); diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java index d6f9664141..813e496c47 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java @@ -87,7 +87,7 @@ public class TeleportToSummon extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = summon.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java index d3c263978c..c9dc34ac22 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java @@ -76,7 +76,7 @@ public class TeleportToTarget extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java index f2148adb54..c7ca6f64cb 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java @@ -65,7 +65,7 @@ public class RollingDice implements IItemHandler final int x = player.getX() + x1; final int y = player.getY() + y1; final int z = player.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); Broadcast.toSelfAndKnownPlayers(player, new Dice(player.getObjectId(), itemId, number, destination.getX(), destination.getY(), destination.getZ())); final SystemMessage sm = new SystemMessage(SystemMessageId.C1_HAS_ROLLED_A_S2); diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/targethandlers/Ground.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/targethandlers/Ground.java index 328c9fb38a..2d4b94e385 100644 --- a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/targethandlers/Ground.java +++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/handlers/targethandlers/Ground.java @@ -52,7 +52,7 @@ public class Ground implements ITargetTypeHandler return null; } - if (!GeoEngine.getInstance().canSeeTarget(creature, worldPosition)) + if (!GeoEngine.getInstance().canSeeLocation(creature, worldPosition)) { if (sendMessage) { diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/Config.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/Config.java index 4fc625ef73..b8540bc8f3 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/Config.java @@ -61,6 +61,7 @@ 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; @@ -932,12 +933,17 @@ public class Config // GeoEngine // -------------------------------------------------- public static Path GEODATA_PATH; + public static GeoType GEODATA_TYPE; public static boolean PATHFINDING; public static String PATHFIND_BUFFERS; - public static float LOW_WEIGHT; - public static float MEDIUM_WEIGHT; - public static float HIGH_WEIGHT; - public static float DIAGONAL_WEIGHT; + public static int MOVE_WEIGHT; + public static int MOVE_WEIGHT_DIAG; + public static int OBSTACLE_WEIGHT; + 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; /** Attribute System */ public static int S_WEAPON_STONE; @@ -2469,12 +2475,17 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); + GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); - MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); - HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); - DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); + MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); + OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); + HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); + MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); + MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); // Load AllowedPlayerRaces config file (if exists) final PropertiesParser AllowedPlayerRaces = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE); diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/commons/util/CommonUtil.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/commons/util/CommonUtil.java index 14ae3a4560..ab7837d827 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/commons/util/CommonUtil.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/commons/util/CommonUtil.java @@ -584,4 +584,15 @@ public class CommonUtil final DecimalFormat formatter = new DecimalFormat(format, new DecimalFormatSymbols(Locale.ENGLISH)); return formatter.format(value); } + + /** + * @param numToTest : The number to test. + * @param min : The minimum limit. + * @param max : The maximum limit. + * @return the number or one of the limit (mininum / maximum). + */ + public static int limit(int numToTest, int min, int max) + { + return (numToTest > max) ? max : ((numToTest < min) ? min : numToTest); + } } diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/commons/util/Point2D.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/commons/util/Point2D.java new file mode 100644 index 0000000000..ebb6272e5e --- /dev/null +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/commons/util/Point2D.java @@ -0,0 +1,180 @@ +/* + * 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 . + */ +package org.l2jmobius.commons.util; + +import java.util.Objects; + +/** + * A datatype used to retain a 2D (x/y) point. It got the capability to be set and cleaned. + */ +public class Point2D +{ + protected volatile int _x; + protected volatile int _y; + + public Point2D(int x, int y) + { + _x = x; + _y = y; + } + + @Override + public Point2D clone() + { + return new Point2D(_x, _y); + } + + @Override + public String toString() + { + return _x + ", " + _y; + } + + @Override + public int hashCode() + { + return Objects.hash(_x, _y); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final Point2D other = (Point2D) obj; + return (_x == other._x) && (_y == other._y); + } + + /** + * @param x : The X coord to test. + * @param y : The Y coord to test. + * @return True if all coordinates equals this {@link Point2D} coordinates. + */ + public boolean equals(int x, int y) + { + return (_x == x) && (_y == y); + } + + public int getX() + { + return _x; + } + + public void setX(int x) + { + _x = x; + } + + public int getY() + { + return _y; + } + + public void setY(int y) + { + _y = y; + } + + public void set(int x, int y) + { + _x = x; + _y = y; + } + + /** + * Refresh the current {@link Point2D} using a reference {@link Point2D} and a distance. The new destination is calculated to go in opposite side of the {@link Point2D} reference.
+ *
+ * This method is perfect to calculate fleeing characters position. + * @param referenceLoc : The Point2D used as position. + * @param distance : The distance to be set between current and new position. + */ + public void setFleeing(Point2D referenceLoc, int distance) + { + final double xDiff = referenceLoc.getX() - _x; + final double yDiff = referenceLoc.getY() - _y; + + final double yxRation = Math.abs(xDiff / yDiff); + + final int y = (int) (distance / (yxRation + 1)); + final int x = (int) (y * yxRation); + + _x += (xDiff < 0 ? x : -x); + _y += (yDiff < 0 ? y : -y); + } + + public void clean() + { + _x = 0; + _y = 0; + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @return The distance between this {@Point2D} and some given coordinates. + */ + public double distance2D(int x, int y) + { + final double dx = (double) _x - x; + final double dy = (double) _y - y; + + return Math.sqrt((dx * dx) + (dy * dy)); + } + + /** + * @param point : The {@link Point2D} to test. + * @return The distance between this {@Point2D} and the {@link Point2D} set as parameter. + */ + public double distance2D(Point2D point) + { + return distance2D(point.getX(), point.getY()); + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of some given coordinates. + */ + public boolean isIn2DRadius(int x, int y, int radius) + { + return distance2D(x, y) < radius; + } + + /** + * @param point : The Point2D to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of the {@link Point2D} set as parameter. + */ + public boolean isIn2DRadius(Point2D point, int radius) + { + return distance2D(point) < radius; + } +} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/AttackableAI.java index 547963e3e8..dc29d4cf6e 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/AttackableAI.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/ai/AttackableAI.java @@ -578,7 +578,7 @@ public class AttackableAI extends CreatureAI } // Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast) - final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); + final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); moveTo(moveLoc.getX(), moveLoc.getY(), moveLoc.getZ()); } } @@ -801,7 +801,7 @@ public class AttackableAI extends CreatureAI final int newZ = npc.getZ() + 30; // Mobius: Verify destination. Prevents wall collision issues and fixes monsters not avoiding obstacles. - moveTo(GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); + moveTo(GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); } return; } diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/data/SpawnTable.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/data/SpawnTable.java index a81201f69e..19f0db5b3e 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/data/SpawnTable.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/data/SpawnTable.java @@ -114,8 +114,8 @@ public class SpawnTable } // XML file for spawn - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); // Write info to XML @@ -192,8 +192,8 @@ public class SpawnTable if (update) { - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = spawn.getNpcSpawnTemplate() != null ? spawn.getNpcSpawnTemplate().getSpawnTemplate().getFile() : new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); final File tempFile = new File(spawnFile.getAbsolutePath().substring(Config.DATAPACK_ROOT.getAbsolutePath().length() + 1).replace('\\', '/') + ".tmp"); try diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/enums/GeoType.java similarity index 69% rename from L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java rename to L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/enums/GeoType.java index 7223b5b2be..f77aa5eac4 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/enums/GeoType.java @@ -14,20 +14,22 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.l2jmobius.gameserver.geoengine.pathfinding; +package org.l2jmobius.gameserver.enums; -/** - * @author -Nemesiss- - */ -public abstract class AbstractNodeLoc +public enum GeoType { - public abstract int getX(); + L2J("%d_%d.l2j"), + L2OFF("%d_%d_conv.dat"); - public abstract int getY(); + private final String _filename; - public abstract int getZ(); + private GeoType(String filename) + { + _filename = filename; + } - public abstract int getNodeX(); - - public abstract int getNodeY(); -} + public String getFilename() + { + return _filename; + } +} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java new file mode 100644 index 0000000000..e62f50a8df --- /dev/null +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java @@ -0,0 +1,144 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 9d255a5f54..7bef9c770b 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,72 +16,63 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.channels.FileChannel.MapMode; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.logging.Level; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; import java.util.logging.Logger; import org.l2jmobius.Config; +import org.l2jmobius.commons.util.CommonUtil; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; -import org.l2jmobius.gameserver.geoengine.geodata.IRegion; -import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; -import org.l2jmobius.gameserver.geoengine.geodata.Region; +import org.l2jmobius.gameserver.enums.GeoType; +import org.l2jmobius.gameserver.enums.MoveDirectionType; +import org.l2jmobius.gameserver.geoengine.geodata.ABlock; +import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; +import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; +import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; +import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.model.interfaces.ILocational; -import org.l2jmobius.gameserver.util.GeoUtils; -import org.l2jmobius.gameserver.util.LinePointIterator; -import org.l2jmobius.gameserver.util.LinePointIterator3D; -/** - * @author -Nemesiss-, HorridoJoho - */ public class GeoEngine { - private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - - private static final int WORLD_MIN_X = -655360; - private static final int WORLD_MIN_Y = -589824; - private static final int WORLD_MIN_Z = -16384; - - /** 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; - - /** The regions array */ - private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; - private static final int ELEVATED_SEE_OVER_DISTANCE = 2; - private static final int MAX_SEE_OVER_HEIGHT = 48; - protected GeoEngine() + private final ABlock[][] _blocks; + private final BlockNull _nullBlock; + + // Pre-allocated buffers. + private final BufferHolder[] _buffers; + + public GeoEngine() { LOGGER.info("GeoEngine: Initializing..."); - for (int i = 0; i < _regions.length(); i++) - { - _regions.set(i, NullRegion.INSTANCE); - } + // Initialize block container. + _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; + + // Load null block. + _nullBlock = new BlockNull(); + + // Initialize multilayer temporarily buffer. + BlockMultilayer.initialize(); + + // Load geo files according to geoengine config setup. int loaded = 0; try { @@ -92,23 +83,52 @@ public class GeoEngine final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); if (Files.exists(geoFilePath)) { - try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + // Region file is load-able, try to load it. + if (loadGeoBlocks(regionX, regionY)) { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); loaded++; } } + else + { + // Region file is not load-able, load null blocks. + loadNullBlocks(regionX, regionY); + } } } } catch (Exception e) { - LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + LOGGER.warning("GeoEngine: Failed to load geodata! " + e); System.exit(1); } - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + // Release multilayer block temporarily buffer. + BlockMultilayer.release(); + + String[] array = Config.PATHFIND_BUFFERS.split(";"); + _buffers = new BufferHolder[array.length]; + + int count = 0; + for (int i = 0; i < array.length; i++) + { + String buf = array[i]; + String[] args = buf.split("x"); + + try + { + int size = Integer.parseInt(args[1]); + count += size; + _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); + } + catch (Exception e) + { + LOGGER.warning("Could not load buffer setting:" + buf + ". " + e); + } + } + LOGGER.info("Loaded " + count + " node buffers."); + // Avoid wrong configs when no files are loaded. if ((loaded == 0) && Config.PATHFINDING) { @@ -118,616 +138,913 @@ public class GeoEngine } /** - * @param geoX - * @param geoY - * @return the region + * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer and log this situation. + * @param size : pre-calculated minimal required size + * @return NodeBuffer : buffer */ - private IRegion getRegion(int geoX, int geoY) + private NodeBuffer getBuffer(int size) { - final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); - if ((region < 0) || (region >= _regions.length())) + NodeBuffer current = null; + for (BufferHolder holder : _buffers) { - return null; - } - return _regions.get(region); - } - - /** - * @param filePath - * @param regionX - * @param regionY - * @throws IOException - */ - 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()).load().order(ByteOrder.LITTLE_ENDIAN))); - } - } - - /** - * @param regionX - * @param regionY - */ - public void unloadRegion(int regionX, int regionY) - { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); - } - - /** - * @param geoX - * @param geoY - * @return if geodata exist - */ - public boolean hasGeoPos(int geoX, int geoY) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return false; - } - return region.hasGeo(); - } - - /** - * Checks the specified position for available geodata. - * @param x the world x - * @param y the world y - * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise - */ - public boolean hasGeo(int x, int y) - { - return hasGeoPos(getGeoX(x), getGeoY(y)); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe check - */ - public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return true; - } - return region.checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe anti-corner cut - */ - public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) - { - boolean can = true; - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - return can && checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the nearest Z value - */ - public int getNearestZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNearestZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next lower Z value - */ - public int getNextLowerZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextLowerZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next higher Z value - */ - public int getNextHigherZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextHigherZ(geoX, geoY, worldZ); - } - - /** - * Gets the Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHeight(int x, int y, int z) - { - return getNearestZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next lower Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getLowerHeight(int x, int y, int z) - { - return getNextLowerZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next higher Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHigherHeight(int x, int y, int z) - { - return getNextHigherZ(getGeoX(x), getGeoY(y), z); - } - - /** - * @param worldX - * @return the geo X - */ - public int getGeoX(int worldX) - { - return (worldX - WORLD_MIN_X) / 16; - } - - /** - * @param worldY - * @return the geo Y - */ - public int getGeoY(int worldY) - { - return (worldY - WORLD_MIN_Y) / 16; - } - - /** - * @param worldZ - * @return the geo Z - */ - public int getGeoZ(int worldZ) - { - return (worldZ - WORLD_MIN_Z) / 16; - } - - /** - * @param geoX - * @return the world X - */ - public int getWorldX(int geoX) - { - return (geoX * 16) + WORLD_MIN_X + 8; - } - - /** - * @param geoY - * @return the world Y - */ - public int getWorldY(int geoY) - { - return (geoY * 16) + WORLD_MIN_Y + 8; - } - - /** - * @param geoZ - * @return the world Z - */ - public int getWorldZ(int geoZ) - { - return (geoZ * 16) + WORLD_MIN_Z + 8; - } - - /** - * Can see target. Doors as target always return true. Checks doors between. - * @param cha the character - * @param target the target - * @return {@code true} if the character can see the target (LOS), {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, WorldObject target) - { - if (target.isDoor()) - { - // Can always see doors. - return true; - } - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); - } - - /** - * Can see target. Checks doors between. - * @param cha the character - * @param worldPosition the world position - * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) - { - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param world - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @param tworld the target's instanceId - * @return - */ - public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) - { - return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instance - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) - { - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) - { - return false; - } - return canSeeTarget(x, y, z, tx, ty, tz); - } - - /** - * @param prevX - * @param prevY - * @param prevGeoZ - * @param curX - * @param curY - * @param nswe - * @return the LOS Z value - */ - private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) - { - if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) - { - throw new RuntimeException("Multiple directions!"); - } - - if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) - { - return getNearestZ(curX, curY, prevGeoZ); - } - - return getNextHigherZ(curX, curY, prevGeoZ); - } - - /** - * Can see target. Does not check doors between. - * @param xValue the x coordinate - * @param yValue the y coordinate - * @param zValue the z coordinate - * @param txValue the target's x coordinate - * @param tyValue the target's y coordinate - * @param tzValue the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) - { - int x = xValue; - int y = yValue; - int tx = txValue; - int ty = tyValue; - - int geoX = getGeoX(x); - int geoY = getGeoY(y); - int tGeoX = getGeoX(tx); - int tGeoY = getGeoY(ty); - - int z = getNearestZ(geoX, geoY, zValue); - int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - // Fastpath. - if ((geoX == tGeoX) && (geoY == tGeoY)) - { - if (hasGeoPos(tGeoX, tGeoY)) - { - return z == tz; - } - return true; - } - - if (tz > z) - { - int tmp = tx; - tx = x; - x = tmp; - - tmp = ty; - ty = y; - y = tmp; - - tmp = tz; - tz = z; - z = tmp; - - tmp = tGeoX; - tGeoX = geoX; - geoX = tmp; - - tmp = tGeoY; - tGeoY = geoY; - geoY = tmp; - } - - final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); - // First point is guaranteed to be available, skip it, we can always see our own position. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - final int prevZ = pointIter.z(); - int prevGeoZ = prevZ; - int ptIndex = 0; - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - - if ((curX == prevX) && (curY == prevY)) + // Find proper size of buffer. + if (holder._size < size) { continue; } - final int beeCurZ = pointIter.z(); - int curGeoZ = prevGeoZ; - - // Check if the position has geodata. - if (hasGeoPos(curX, curY)) + // Find unlocked NodeBuffer. + for (NodeBuffer buffer : holder._buffer) { - final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); - curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); - int maxHeight; - if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) + if (!buffer.isLocked()) { - maxHeight = z + MAX_SEE_OVER_HEIGHT; - } - else - { - maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; + continue; } - boolean canSeeThrough = false; - if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) + return buffer; + } + + LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); + + // NodeBuffer not found, allocate temporary buffer. + current = new NodeBuffer(holder._size); + current.isLocked(); + } + + return current; + } + + /** + * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + * @return boolean : True, when geodata file was loaded without problem. + */ + private boolean loadGeoBlocks(int regionX, int regionY) + { + final String filename = String.format(Config.GEODATA_TYPE.getFilename(), regionX, regionY); + final String filepath = Config.GEODATA_PATH + "\\" + filename; + + // Standard load. + try (RandomAccessFile raf = new RandomAccessFile(filepath, "r"); + FileChannel fc = raf.getChannel()) + { + // Initialize file buffer. + MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // Load 18B header for L2OFF geodata (1st and 2nd byte...region X and Y). + if (Config.GEODATA_TYPE == GeoType.L2OFF) + { + for (int i = 0; i < 18; i++) { - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) + buffer.get(); + } + } + + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Loop over region blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + if (Config.GEODATA_TYPE == GeoType.L2J) { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) - { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); + // Get block type. + final byte type = buffer.get(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + case GeoStructure.TYPE_MULTILAYER_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + default: + { + throw new IllegalArgumentException("Unknown block type: " + type); + } + } } else { - canSeeThrough = true; + // Get block type. + final short type = buffer.getShort(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + default: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + } } } + } + + // Check data consistency. + if (buffer.remaining() > 0) + { + LOGGER.warning("Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); + } + + // Loading was successful. + return true; + } + catch (Exception e) + { + // An error occured while loading, load null blocks. + LOGGER.warning("Error loading " + filename + " region file. " + e); + + // Replace whole region file with null blocks. + loadNullBlocks(regionX, regionY); + + // Loading was not successful. + return false; + } + } + + /** + * Loads null blocks. Used when no region file is detected or an error occurs during loading. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + */ + private void loadNullBlocks(int regionX, int regionY) + { + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Load all null blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + _blocks[blockX + ix][blockY + iy] = _nullBlock; + } + } + } + + /** + * Converts world X to geodata X. + * @param worldX + * @return int : Geo X + */ + public static int getGeoX(int worldX) + { + return (worldX - World.WORLD_X_MIN) >> 4; + } + + /** + * Converts world Y to geodata Y. + * @param worldY + * @return int : Geo Y + */ + public static int getGeoY(int worldY) + { + return (worldY - World.WORLD_Y_MIN) >> 4; + } + + /** + * Converts geodata X to world X. + * @param geoX + * @return int : World X + */ + public static int getWorldX(int geoX) + { + return (geoX << 4) + World.WORLD_X_MIN + 8; + } + + /** + * Converts geodata Y to world Y. + * @param geoY + * @return int : World Y + */ + public static int getWorldY(int geoY) + { + return (geoY << 4) + World.WORLD_Y_MIN + 8; + } + + /** + * Returns block of geodata on given coordinates. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return {@link ABlock} : Block of geodata. + */ + public ABlock getBlock(int geoX, int geoY) + { + final int x = geoX / GeoStructure.BLOCK_CELLS_X; + if ((x < 0) || (x >= GeoStructure.GEO_BLOCKS_X)) + { + return null; + } + final int y = geoY / GeoStructure.BLOCK_CELLS_Y; + if ((y < 0) || (y >= GeoStructure.GEO_BLOCKS_Y)) + { + return null; + } + return _blocks[x][y]; + } + + /** + * Check if geo coordinates has geo. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return boolean : True, if given geo coordinates have geodata + */ + public boolean hasGeoPos(int geoX, int geoY) + { + final ABlock block = getBlock(geoX, geoY); + return (block != null) && block.hasGeoPos(); + } + + /** + * Returns the height of cell, which is closest to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell geodata Z coordinate, closest to given coordinates. + */ + public short getHeightNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return (short) worldZ; + } + return block.getHeightNearest(geoX, geoY, worldZ); + } + + /** + * Returns the NSWE flag byte of cell, which is closes to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. + */ + public byte getNsweNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return GeoStructure.CELL_FLAG_ALL; + } + return block.getNsweNearest(geoX, geoY, worldZ); + } + + /** + * Check if world coordinates has geo. + * @param worldX : World X + * @param worldY : World Y + * @return boolean : True, if given world coordinates have geodata + */ + public boolean hasGeo(int worldX, int worldY) + { + return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param loc : The location used as reference. + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(Location loc) + { + return getHeightNearest(getGeoX(loc.getX()), getGeoY(loc.getY()), loc.getZ()); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param worldX : world x + * @param worldY : world y + * @param worldZ : world z + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(int worldX, int worldY, int worldZ) + { + return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); + } + + /** + * Check line of sight from {@link WorldObject} to {@link WorldObject}.
+ * @param object : The origin object. + * @param target : The target object. + * @return True, when object can see target. + */ + public boolean canSeeTarget(WorldObject object, WorldObject target) + { + // Can always see doors. + if (target.isDoor()) + { + return true; + } + + if (object.getInstanceWorld() != target.getInstanceWorld()) + { + return false; + } + + if (DoorData.getInstance().checkIfDoorsBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld())) + { + return false; + } + + // Get object's and target's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + double theight = 0; + if (target instanceof Creature) + { + theight += (((Creature) target).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + return canSee(object.getX(), object.getY(), object.getZ(), oheight, target.getX(), target.getY(), target.getZ(), theight) && canSee(target.getX(), target.getY(), target.getZ(), theight, object.getX(), object.getY(), object.getZ(), oheight); + } + + /** + * Check line of sight from {@link WorldObject} to {@link Location}.
+ * Note: The check uses {@link Location}'s real Z coordinate (e.g. point above ground), not its geodata representation. + * @param object : The origin object. + * @param position : The target position. + * @return True, when object can see position. + */ + public boolean canSeeLocation(WorldObject object, Location position) + { + // Get object and location coordinates. + int ox = object.getX(); + int oy = object.getY(); + int oz = object.getZ(); + int tx = position.getX(); + int ty = position.getY(); + int tz = position.getZ(); + + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld())) + { + return false; + } + + // Get object's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + // Perform geodata check. + return canSee(ox, oy, oz, oheight, tx, ty, tz, 0) && canSee(tx, ty, tz, 0, ox, oy, oz, oheight); + } + + /** + * Simple check for origin to target visibility.
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param oheight : The height of origin, used as start point. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param theight : The height of target, used as end point. + * @return True, when origin can see target. + */ + public boolean canSee(int ox, int oy, int oz, double oheight, int tx, int ty, int tz, double theight) + { + // Get origin geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get target geodata coordinates. + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check being on same cell and layer (index). + // Note: Get index must use origin height increased by cell height, the method returns index to height exclusive self. + int index = block.getIndexBelow(gox, goy, oz + GeoStructure.CELL_HEIGHT); + if (index < 0) + { + return false; + } + + if ((gox == gtx) && (goy == gty)) + { + return index == block.getIndexBelow(gtx, gty, tz + GeoStructure.CELL_HEIGHT); + } + + // Get ground and nswe flag. + int groundZ = block.getHeight(index); + int nswe = block.getNswe(index); + + // Get delta coordinates, slope of line (XY, XZ) and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double dz = (tz + theight) - (oz + oheight); + final double m = (double) dy / dx; + final double mz = dz / Math.sqrt((dx * dx) + (dy * dy)); + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); + + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) + { + // Set next cell in X direction. + gridX += mdt.getStepX(); + gox += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); - if (!canSeeThrough) - { - return false; - } + // Set next cell in Y direction. + gridY += mdt.getStepY(); + goy += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevGeoZ = curGeoZ; - ++ptIndex; + // Get block of the next cell. + block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get line of sight height (including Z slope). + double losz = oz + oheight + Config.MAX_OBSTACLE_HEIGHT; + losz += mz * Math.sqrt(((checkX - ox) * (checkX - ox)) + ((checkY - oy) * (checkY - oy))); + + // Check line of sight going though wall (vertical check). + + // Get index of particular layer, based on last iterated cell conditions. + boolean canMove = (nswe & dir) != 0; + if (canMove) + { + // No wall present, get next cell below current cell. + index = block.getIndexBelow(gox, goy, groundZ + GeoStructure.CELL_IGNORE_HEIGHT); + } + else + { + // Wall present, get next cell above current cell. + index = block.getIndexAbove(gox, goy, groundZ - (2 * GeoStructure.CELL_HEIGHT)); + } + + // Next cell's does not exist (no geodata with valid condition), return fail. + if (index < 0) + { + return false; + } + + // Get next cell's layer height. + int z = block.getHeight(index); + + // Perform sine of sight check (next cell is above line of sight line), return fail. + if (z > losz) + { + return false; + } + + // Next cell is accessible, update z and NSWE. + groundZ = z; + nswe = block.getNswe(index); } + // Iteration is completed, no obstacle is found. return true; } /** - * Move check. - * @param x the x coordinate - * @param y the y coordinate - * @param zValue the z coordinate - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tzValue the target's z coordinate - * @param instance the instance - * @return the last Location (x,y,z) where player can walk - just before wall + * Check movement from coordinates to coordinates.
+ * Note: The Z coordinates are supposed to be already validated geodata coordinates. + * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return True, when target coordinates are reachable from origin coordinates. */ - public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(x); - final int geoY = getGeoY(y); - final int z = getNearestZ(geoX, geoY, zValue); - final int tGeoX = getGeoX(tx); - final int tGeoY = getGeoY(ty); - final int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return new Location(x, y, getHeight(x, y, z)); - } - if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) - { - return new Location(x, y, getHeight(x, y, z)); + return false; } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = z; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return false; + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + int goz = getHeightNearest(gox, goy, oz); + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check movement within same cell. + if ((gox == gtx) && (goy == gty)) + { + return goz == getHeight(tx, ty, tz); + } + + // Get nswe flag. + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - // Can't move, return previous location. - return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check point heading into obstacle, if so return current point. + if ((nswe & dir) == 0) + { + return false; + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return false; + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != tz)) - { - // Different floors, return start location. - return new Location(x, y, z); - } - - return new Location(tx, ty, tz); + // When origin Z is target Z, the move is successful. + return goz == getHeight(tx, ty, tz); } /** - * Checks if its possible to move from one location to another. - * @param fromX the X coordinate to start checking from - * @param fromY the Y coordinate to start checking from - * @param fromZvalue the Z coordinate to start checking from - * @param toX the X coordinate to end checking at - * @param toY the Y coordinate to end checking at - * @param toZvalue the Z coordinate to end checking at - * @param instance the instance - * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + * Check movement from origin to target coordinates. Returns last available point in the checked path.
+ * Target X and Y reachable and Z is on same floor: + *
    + *
  • Location of the target with corrected Z value from geodata.
  • + *
+ * Target X and Y reachable but Z is on another floor: + *
    + *
  • Location of the origin with corrected Z value from geodata.
  • + *
+ * Target X and Y not reachable: + *
    + *
  • Last accessible location in destination to target.
  • + *
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return The {@link Location} representing last point of movement (e.g. just before wall). */ - public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + public Location getValidLocation(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(fromX); - final int geoY = getGeoY(fromY); - final int fromZ = getNearestZ(geoX, geoY, fromZvalue); - final int tGeoX = getGeoX(toX); - final int tGeoY = getGeoY(toY); - final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); - - if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) - { - return false; + return new Location(ox, oy, oz); } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = fromZ; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return new Location(ox, oy, oz); + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + final int gtz = getHeightNearest(gtx, gty, tz); + int goz = getHeightNearest(gox, goy, oz); + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - return false; - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check target cell is out of geodata grid (world coordinates). + if ((nx < 0) || (nx >= GeoStructure.GEO_CELLS_X) || (ny < 0) || (ny >= GeoStructure.GEO_CELLS_Y)) + { + return new Location(checkX, checkY, goz); + } + + // Check point heading into obstacle, if so return current (border) point. + if ((nswe & dir) == 0) + { + return new Location(checkX, checkY, goz); + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current (border) point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return new Location(checkX, checkY, goz); + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) - { - // Different floors. - return false; - } - - return true; + // Compare Z coordinates: + // If same, path is okay, return target point and fix its Z geodata coordinate. + // If not same, path is does not exist, return origin point. + return goz == gtz ? new Location(tx, ty, gtz) : new Location(ox, oy, oz); } + /** + * Returns the list of location objects as a result of complete path calculation. + * @param ox : origin x + * @param oy : origin y + * @param oz : origin z + * @param tx : target x + * @param ty : target y + * @param tz : target z + * @param instance + * @return {@code List} : complete path from nodes + */ + public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + { + // Get origin and check existing geo coords. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + if (!hasGeoPos(gox, goy)) + { + return Collections.emptyList(); + } + + int goz = getHeightNearest(gox, goy, oz); + + // Get target and check existing geo coords. + int gtx = getGeoX(tx); + int gty = getGeoY(ty); + if (!hasGeoPos(gtx, gty)) + { + return Collections.emptyList(); + } + + int gtz = getHeightNearest(gtx, gty, tz); + + // Prepare buffer for pathfinding calculations. + NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + if (buffer == null) + { + return Collections.emptyList(); + } + + // Find path. + List path = null; + try + { + path = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + if (path.isEmpty()) + { + return Collections.emptyList(); + } + } + catch (Exception e) + { + return Collections.emptyList(); + } + finally + { + buffer.free(); + } + + // Check path. + if (path.size() < 3) + { + return path; + } + + // Get path list iterator. + ListIterator point = path.listIterator(); + + // Get node A (origin). + int nodeAx = ox; + int nodeAy = oy; + int nodeAz = goz; + + // Get node B. + Location nodeB = point.next(); + + // Iterate thought the path to optimize it. + while (point.hasNext()) + { + // Get node C. + Location nodeC = path.get(point.nextIndex()); + + // Check movement from node A to node C. + if (canMoveToTarget(nodeAx, nodeAy, nodeAz, nodeC.getX(), nodeC.getY(), nodeC.getZ(), instance)) + { + // Can move from node A to node C. + // Remove node B. + point.remove(); + } + else + { + // Can not move from node A to node C. + // Set node A (node B is part of path, update A coordinates). + nodeAx = nodeB.getX(); + nodeAy = nodeB.getY(); + nodeAz = nodeB.getZ(); + } + + // Set node B. + nodeB = point.next(); + } + + return path; + } + + /** + * NodeBuffer container with specified size and count of separate buffers. + */ + private static class BufferHolder + { + final int _size; + final List _buffer; + + public BufferHolder(int size, int count) + { + _size = size; + _buffer = new ArrayList<>(count); + + for (int i = 0; i < count; i++) + { + _buffer.add(new NodeBuffer(size)); + } + } + } + + /** + * Returns the instance of the {@link GeoEngine}. + * @return {@link GeoEngine} : The instance. + */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -737,4 +1054,4 @@ public class GeoEngine { protected static final GeoEngine INSTANCE = new GeoEngine(); } -} +} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java deleted file mode 100644 index cc76b7523a..0000000000 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ /dev/null @@ -1,301 +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 . - */ -package org.l2jmobius.gameserver.geoengine; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; -import org.l2jmobius.gameserver.model.World; -import org.l2jmobius.gameserver.model.instancezone.Instance; - -/** - * @author -Nemesiss- - */ -public class GeoEnginePathfinding -{ - private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); - - private BufferInfo[] _buffers; - - protected GeoEnginePathfinding() - { - try - { - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - - _buffers = 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); - } - - _buffers[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"); - } - } - - public boolean pathNodesExist(short regionoffset) - { - return false; - } - - public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) - { - 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)))); - if (buffer == null) - { - return null; - } - - List path = null; - try - { - final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); - - if (result == null) - { - return null; - } - - path = constructPath(result); - } - catch (Exception e) - { - LOGGER.warning(e.getMessage()); - return null; - } - finally - { - buffer.free(); - } - - // check path - if (path.size() < 3) - { - return path; - } - - int currentX, currentY, currentZ; - ListIterator middlePoint; - - middlePoint = path.listIterator(); - currentX = x; - currentY = y; - currentZ = z; - - while (middlePoint.hasNext()) - { - final AbstractNodeLoc locMiddle = middlePoint.next(); - if (!middlePoint.hasNext()) - { - break; - } - - final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); - if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) - { - middlePoint.remove(); - } - else - { - currentX = locMiddle.getX(); - currentY = locMiddle.getY(); - currentZ = locMiddle.getZ(); - } - } - - return path; - } - - /** - * Convert geodata position to pathnode position - * @param geo_pos - * @return pathnode position - */ - public short getNodePos(int geo_pos) - { - return (short) (geo_pos >> 3); // OK? - } - - /** - * Convert node position to pathnode block position - * @param node_pos - * @return pathnode block position (0...255) - */ - public short getNodeBlock(int node_pos) - { - return (short) (node_pos % 256); - } - - public byte getRegionX(int node_pos) - { - return (byte) ((node_pos >> 8) + World.TILE_X_MIN); - } - - public byte getRegionY(int node_pos) - { - return (byte) ((node_pos >> 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 node_x rx - * @return - */ - public int calculateWorldX(short node_x) - { - return World.MAP_MIN_X + (node_x * 128) + 48; - } - - /** - * Convert pathnode y to World y position - * @param node_y - * @return - */ - public int calculateWorldY(short node_y) - { - return World.MAP_MIN_Y + (node_y * 128) + 48; - } - - private List constructPath(AbstractNode nodeValue) - { - final LinkedList path = new LinkedList<>(); - int previousDirectionX = Integer.MIN_VALUE; - int previousDirectionY = Integer.MIN_VALUE; - int directionX, directionY; - - AbstractNode node = nodeValue; - while (node.getParent() != null) - { - directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); - directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); - - // only add a new route point if moving direction changes - if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) - { - previousDirectionX = directionX; - previousDirectionY = directionY; - - path.addFirst(node.getLoc()); - node.setLoc(null); - } - - node = node.getParent(); - } - - return path; - } - - private CellNodeBuffer alloc(int size) - { - CellNodeBuffer current = null; - for (BufferInfo i : _buffers) - { - if (i.mapSize >= size) - { - for (CellNodeBuffer buf : i.buffer) - { - if (buf.lock()) - { - current = buf; - break; - } - } - if (current != null) - { - break; - } - - // not found, allocate temporary buffer - current = new CellNodeBuffer(i.mapSize); - current.lock(); - if (i.buffer.size() < i.count) - { - i.buffer.add(current); - break; - } - } - } - - return current; - } - - private static final class BufferInfo - { - final int mapSize; - final int count; - ArrayList buffer; - - public BufferInfo(int size, int cnt) - { - mapSize = size; - count = cnt; - buffer = new ArrayList<>(count); - } - } - - public static GeoEnginePathfinding getInstance() - { - return SingletonHolder.INSTANCE; - } - - private static class SingletonHolder - { - protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); - } -} diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java new file mode 100644 index 0000000000..49ec35aa1c --- /dev/null +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java @@ -0,0 +1,85 @@ +/* + * 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 . + */ +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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @param index : Index of the cell. + * @return short : Cell geodata Z coordinate, below given coordinates. + */ + public abstract byte getNswe(int index); +} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java new file mode 100644 index 0000000000..f5997bf287 --- /dev/null +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java @@ -0,0 +1,130 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java new file mode 100644 index 0000000000..9af9d2a28a --- /dev/null +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java @@ -0,0 +1,95 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java new file mode 100644 index 0000000000..31fbf5d477 --- /dev/null +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java similarity index 55% rename from L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java rename to L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java index 72a5a635c7..f77d21d84b 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java @@ -16,40 +16,53 @@ */ package org.l2jmobius.gameserver.geoengine.geodata; -/** - * @author HorridoJoho - */ -public final class NullRegion implements IRegion +public class BlockNull extends ABlock { - public static final NullRegion INSTANCE = new NullRegion(); - @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 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() + public boolean hasGeoPos() { 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java deleted file mode 100644 index 61ea4fcf82..0000000000 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java +++ /dev/null @@ -1,48 +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 . - */ -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() - { - } -} diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java deleted file mode 100644 index 3c8de1a7f7..0000000000 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java +++ /dev/null @@ -1,77 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java deleted file mode 100644 index 1ccdefe3da..0000000000 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java +++ /dev/null @@ -1,56 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java new file mode 100644 index 0000000000..691b164e0c --- /dev/null +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ +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_ALL = 0x0F; + + // 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; +} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java deleted file mode 100644 index 25eafc5a04..0000000000 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java +++ /dev/null @@ -1,42 +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 . - */ -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); -} diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java deleted file mode 100644 index 0d2c765002..0000000000 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java +++ /dev/null @@ -1,47 +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 . - */ -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(); -} diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java deleted file mode 100644 index 52df457360..0000000000 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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); - - // 1 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; - } -} diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java deleted file mode 100644 index fc924cb875..0000000000 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java +++ /dev/null @@ -1,92 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java deleted file mode 100644 index 5087513ed9..0000000000 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java +++ /dev/null @@ -1,87 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public abstract class AbstractNode -{ - private T _loc; - private AbstractNode _parent; - - public AbstractNode(T loc) - { - _loc = loc; - } - - public void setParent(AbstractNode p) - { - _parent = p; - } - - public AbstractNode getParent() - { - return _parent; - } - - public T getLoc() - { - return _loc; - } - - public void setLoc(T l) - { - _loc = l; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); - return result; - } - - @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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java deleted file mode 100644 index 3425234e0e..0000000000 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public class CellNode extends AbstractNode -{ - 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java deleted file mode 100644 index 522610bb7c..0000000000 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java +++ /dev/null @@ -1,348 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -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 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) - { - _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(); - } - - public final List debugPath() - { - final List 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 void getNeighbors() - { - if (!_current.getLoc().canGoAll()) - { - 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); - } - - // SouthEast - if ((nodeE != null) && (nodeS != null)) - { - if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) - { - addNode(x + 1, y + 1, z, true); - } - } - - // SouthWest - if ((nodeS != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) - { - addNode(x - 1, y + 1, z, true); - } - } - - // NorthEast - if ((nodeN != null) && (nodeE != null)) - { - if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) - { - addNode(x + 1, y - 1, z, true); - } - } - - // NorthWest - if ((nodeN != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) - { - addNode(x - 1, y - 1, z, true); - } - } - } - - private 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(); - // reinit node if needed - if (result.getLoc() != null) - { - result.getLoc().set(x, y, z); - } - else - { - result.setLoc(new NodeLoc(x, y, z)); - } - } - - return result; - } - - private 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 boolean isHighWeight(int x, int y, int z) - { - final CellNode result = getNode(x, y, z); - if (result == null) - { - return true; - } - - if (!result.getLoc().canGoAll()) - { - return true; - } - if (Math.abs(result.getLoc().getZ() - z) > 16) - { - return true; - } - - return false; - } - - private 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java new file mode 100644 index 0000000000..fa5ca43c2f --- /dev/null +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -0,0 +1,111 @@ +/* + * 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 . + */ +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 geodata values. + private int _geoX; + private int _geoY; + private byte _nswe; + + // 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; + + _costG = 0; + _costH = 0; + _costF = 0; + + _parent = null; + } + + public void setGeo(int gx, int gy, int gz, byte nswe) + { + super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); + + _geoX = gx; + _geoY = gy; + _nswe = nswe; + } + + 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 int getCostF() + { + return _costF; + } + + public Node getParent() + { + return _parent; + } + + @Override + public int compareTo(Node o) + { + return _costF - o._costF; + } +} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java new file mode 100644 index 0000000000..b464212c96 --- /dev/null +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -0,0 +1,368 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.awt.Color; +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; +import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; + +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 _opened; + // Container holding Nodes already explored. + private final List _closed; + + // Target coordinates. + private int _gtx; + private int _gty; + private int _gtz; + + // Pathfinding statistics. + private long _timeStamp; + private long _lastElapsedTime; + + 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 findPath(int gox, int goy, int goz, int gtx, int gty, int gtz) + { + // Set start timestamp. + _timeStamp = System.currentTimeMillis(); + + // 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)); + _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 constructPath() + { + // Create result. + final LinkedList 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; + } + + /** + * Creates list of Nodes to show debug path. + * @param debug : The debug packet to add debug informations in. + */ + public void debugPath(ExServerPrimitive debug) + { + // Add all opened node as yellow points. + for (Node n : _opened) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); + } + + // Add all opened node as blue points. + for (Node n : _closed) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); + } + } + + 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; + + _lastElapsedTime = System.currentTimeMillis() - _timeStamp; + _lock.unlock(); + } + + public long getElapsedTime() + { + return _lastElapsedTime; + } + + /** + * Expand the current {@link Node} by exploring its neighbors (axially and diagonally). + */ + private void expand() + { + // Movement is blocked, skip. + final byte nswe = _current.getNSWE(); + if (nswe == 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; + + // Can move north, expand. + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + } + + // Can move south, expand. + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + } + + // Can move west, expand. + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move east, expand. + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move north-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move north-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + } + + /** + * 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 weight : The weight of movement to the new node. + * @return The nswe of the added node. Blank, if not added. + */ + private byte addNode(int gx, int gy, int gzValue, int weight) + { + // 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]; + + // Set node geodata coordinates. + node.setGeo(gx, gy, gz, nswe); + + // 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, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + _opened.add(node); + _bufferIndex++; + return nswe; + } + + /** + * Calculate cost H value, calculated using diagonal distance method.
+ * 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. + final int dx = Math.abs(gx - _gtx); + final int dy = Math.abs(gy - _gty); + final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + + // Get diagonal and axial differences to the target. + final int dd = Math.min(dx, dy); + final int da = Math.max(dx, dy) - dd; + + // Calculate the diagonal distance of the node to the target. + return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + } +} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java deleted file mode 100644 index e3bad04b25..0000000000 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; - -/** - * @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 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 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; - // return super.hashCode(); - } - - @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; - if (_x != other._x) - { - return false; - } - if (_y != other._y) - { - return false; - } - if (_goNorth != other._goNorth) - { - return false; - } - if (_goEast != other._goEast) - { - return false; - } - if (_goSouth != other._goSouth) - { - return false; - } - if (_goWest != other._goWest) - { - return false; - } - if (_geoHeight != other._geoHeight) - { - return false; - } - return true; - } -} diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java index 1faf1ae423..ed261765e1 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java @@ -93,15 +93,15 @@ public class ZoneManager implements IXmlReader private static final Map SETTINGS = new HashMap<>(); private static final int SHIFT_BY = 15; - private static final int OFFSET_X = Math.abs(World.MAP_MIN_X >> SHIFT_BY); - private static final int OFFSET_Y = Math.abs(World.MAP_MIN_Y >> SHIFT_BY); + private static final int OFFSET_X = Math.abs(World.WORLD_X_MIN >> SHIFT_BY); + private static final int OFFSET_Y = Math.abs(World.WORLD_Y_MIN >> SHIFT_BY); private final Map, ConcurrentHashMap> _classZones = new ConcurrentHashMap<>(); private final Map _spawnTerritories = new ConcurrentHashMap<>(); private final AtomicInteger _lastDynamicId = new AtomicInteger(300000); private List _debugItems; - private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.MAP_MAX_X >> SHIFT_BY) + OFFSET_X + 1][(World.MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y + 1]; + private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.WORLD_X_MAX >> SHIFT_BY) + OFFSET_X + 1][(World.WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y + 1]; /** * Instantiates a new zone manager. diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/Location.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/Location.java index 73689ab1a6..ca3b22a3f0 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/Location.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/Location.java @@ -16,30 +16,30 @@ */ package org.l2jmobius.gameserver.model; +import java.util.Objects; + +import org.l2jmobius.commons.util.Point2D; import org.l2jmobius.gameserver.model.interfaces.ILocational; import org.l2jmobius.gameserver.model.interfaces.IPositionable; /** - * Location data transfer object.
- * Contains coordinates data, heading and instance Id. - * @author Zoey76 + * A datatype used to retain a 3D (x/y/z/heading) point. It got the capability to be set and cleaned. */ -public class Location implements IPositionable +public class Location extends Point2D implements IPositionable { - protected int _x; - protected int _y; - protected int _z; - private int _heading; + protected volatile int _z; + protected volatile int _heading; public Location(int x, int y, int z) { - this(x, y, z, 0); + super(x, y); + _z = z; + _heading = 0; } public Location(int x, int y, int z, int heading) { - _x = x; - _y = y; + super(x, y); _z = z; _heading = heading; } @@ -51,9 +51,8 @@ public class Location implements IPositionable public Location(StatSet set) { - _x = set.getInt("x"); - _y = set.getInt("y"); - _z = set.getInt("z"); + super(set.getInt("x", 0), set.getInt("y", 0)); + _z = set.getInt("z", 0); _heading = set.getInt("heading", 0); } @@ -146,6 +145,25 @@ public class Location implements IPositionable _heading = loc.getHeading(); } + @Override + public void clean() + { + super.clean(); + _z = 0; + } + + @Override + public Location clone() + { + return new Location(_x, _y, _z); + } + + @Override + public int hashCode() + { + return (31 * super.hashCode()) + Objects.hash(_z); + } + @Override public boolean equals(Object obj) { diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/World.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/World.java index c1510e8c0e..0d796dd834 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/World.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/World.java @@ -66,19 +66,19 @@ public class World public static final int TILE_Y_MAX = 26; public static final int TILE_ZERO_COORD_X = 20; public static final int TILE_ZERO_COORD_Y = 18; - public static final int MAP_MIN_X = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; - public static final int MAP_MIN_Y = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; + public static final int WORLD_X_MIN = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; + public static final int WORLD_Y_MIN = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; - public static final int MAP_MAX_X = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; - public static final int MAP_MAX_Y = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; + public static final int WORLD_X_MAX = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; + public static final int WORLD_Y_MAX = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; /** Calculated offset used so top left region is 0,0 */ - public static final int OFFSET_X = Math.abs(MAP_MIN_X >> SHIFT_BY); - public static final int OFFSET_Y = Math.abs(MAP_MIN_Y >> SHIFT_BY); + public static final int OFFSET_X = Math.abs(WORLD_X_MIN >> SHIFT_BY); + public static final int OFFSET_Y = Math.abs(WORLD_Y_MIN >> SHIFT_BY); /** Number of regions. */ - private static final int REGIONS_X = (MAP_MAX_X >> SHIFT_BY) + OFFSET_X; - private static final int REGIONS_Y = (MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y; + private static final int REGIONS_X = (WORLD_X_MAX >> SHIFT_BY) + OFFSET_X; + private static final int REGIONS_Y = (WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y; /** Map containing all the players in game. */ private static final Map _allPlayers = new ConcurrentHashMap<>(); @@ -817,9 +817,6 @@ public class World return _memberInPartyNumber.get(); } - /** - * @return the current instance of World - */ public static World getInstance() { return SingletonHolder.INSTANCE; diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/WorldObject.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/WorldObject.java index 0e5536bbc0..62313ffea1 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/WorldObject.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/WorldObject.java @@ -188,23 +188,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif synchronized (this) { int spawnX = x; - if (spawnX > World.MAP_MAX_X) + if (spawnX > World.WORLD_X_MAX) { - spawnX = World.MAP_MAX_X - 5000; + spawnX = World.WORLD_X_MAX - 5000; } - if (spawnX < World.MAP_MIN_X) + if (spawnX < World.WORLD_X_MIN) { - spawnX = World.MAP_MIN_X + 5000; + spawnX = World.WORLD_X_MIN + 5000; } int spawnY = y; - if (spawnY > World.MAP_MAX_Y) + if (spawnY > World.WORLD_Y_MAX) { - spawnY = World.MAP_MAX_Y - 5000; + spawnY = World.WORLD_Y_MAX - 5000; } - if (spawnY < World.MAP_MIN_Y) + if (spawnY < World.WORLD_Y_MIN) { - spawnY = World.MAP_MIN_Y + 5000; + spawnY = World.WORLD_Y_MIN + 5000; } // Set the x,y,z position of the WorldObject. If flagged with _isSpawned, setXYZ will automatically update world region, so avoid that. @@ -518,23 +518,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif public void setXYZInvisible(int x, int y, int z) { int correctX = x; - if (correctX > World.MAP_MAX_X) + if (correctX > World.WORLD_X_MAX) { - correctX = World.MAP_MAX_X - 5000; + correctX = World.WORLD_X_MAX - 5000; } - if (correctX < World.MAP_MIN_X) + if (correctX < World.WORLD_X_MIN) { - correctX = World.MAP_MIN_X + 5000; + correctX = World.WORLD_X_MIN + 5000; } int correctY = y; - if (correctY > World.MAP_MAX_Y) + if (correctY > World.WORLD_Y_MAX) { - correctY = World.MAP_MAX_Y - 5000; + correctY = World.WORLD_Y_MAX - 5000; } - if (correctY < World.MAP_MIN_Y) + if (correctY < World.WORLD_Y_MIN) { - correctY = World.MAP_MIN_Y + 5000; + correctY = World.WORLD_Y_MIN + 5000; } setXYZ(correctX, correctY, z); diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/Creature.java index c1f646d842..aef8805a04 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -65,8 +65,6 @@ 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.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -2585,7 +2583,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3406,8 +3404,8 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe final int originalX = x; final int originalY = y; final int originalZ = z; - final int gtx = (originalX - World.MAP_MIN_X) >> 4; - final int gty = (originalY - World.MAP_MIN_Y) >> 4; + final int gtx = (originalX - World.WORLD_X_MIN) >> 4; + final int gty = (originalY - World.WORLD_Y_MIN) >> 4; if (isOnGeodataPath()) { try @@ -3430,7 +3428,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe && !(((curZ - z) > 300) && (distance < 300))) // Prohibit correcting destination if character wants to fall. { // location different if destination wasn't reached (or just z coord is different) - final Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(curX, curY, curZ, x, y, z, getInstanceWorld()); + final Location destiny = GeoEngine.getInstance().getValidLocation(curX, curY, curZ, x, y, z, getInstanceWorld()); x = destiny.getX(); y = destiny.getY(); z = destiny.getZ(); @@ -3444,7 +3442,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { if (isPlayer() && !_isFlying && !isInWater) diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/Summon.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/Summon.java index 6065b24f61..919cda941e 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/Summon.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/actor/Summon.java @@ -106,7 +106,7 @@ public abstract class Summon extends Playable final int x = owner.getX(); final int y = owner.getY(); final int z = owner.getZ(); - final Location location = GeoEngine.getInstance().canMoveToTargetLoc(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, owner.getInstanceWorld()); + final Location location = GeoEngine.getInstance().getValidLocation(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, getInstanceWorld()); setXYZInvisible(location.getX(), location.getY(), location.getZ()); } diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java index d6958b7a37..46adfaa5af 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java @@ -1502,7 +1502,7 @@ public class ItemInstance extends WorldObject if (dropper != null) { final Instance instance = dropper.getInstanceWorld(); - final Location dropDest = GeoEngine.getInstance().canMoveToTargetLoc(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); + final Location dropDest = GeoEngine.getInstance().getValidLocation(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); x = dropDest.getX(); y = dropDest.getY(); z = dropDest.getZ(); diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java index ae60afda33..e7fb35337e 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java @@ -1179,7 +1179,7 @@ public class SkillCaster implements Runnable } } - final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().canMoveToTargetLoc(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); + final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().getValidLocation(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); creature.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); creature.broadcastPacket(new FlyToLocation(creature, destination, flyType, 0, 0, 333)); creature.setXYZ(destination); diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java index c62c821f44..30068134e3 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java @@ -114,7 +114,7 @@ public class ValidatePosition implements IClientIncomingPacket { if (player.isFalling(_z)) { - final int nearestZ = GeoEngine.getInstance().getHigherHeight(_x, _y, _z); + final int nearestZ = GeoEngine.getInstance().getHeight(_x, _y, _z); if (player.getZ() < nearestZ) { player.setXYZ(_x, _y, nearestZ); diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/util/GeoUtils.java index 4600a0fc53..19a979819e 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -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.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; @@ -30,21 +30,21 @@ public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getInstance().getWorldX(iter.x()); - final int wy = GeoEngine.getInstance().getWorldY(iter.y()); + final int wx = GeoEngine.getWorldX(iter.x()); + final int wy = GeoEngine.getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public final class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); - int wy = GeoEngine.getInstance().getWorldY(prevY); + int wx = GeoEngine.getWorldX(prevX); + int wy = GeoEngine.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.getInstance().getWorldX(curX); - wy = GeoEngine.getInstance().getWorldY(curY); + wx = GeoEngine.getWorldX(curX); + wy = GeoEngine.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().checkNearestNswe(x, y, z, nswe)) + if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0) { return Color.GREEN; } @@ -109,9 +109,8 @@ public final class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final GeoEngine ge = GeoEngine.getInstance(); - final int playerGx = ge.getGeoX(player.getX()); - final int playerGy = ge.getGeoY(player.getY()); + final int playerGx = GeoEngine.getGeoX(player.getX()); + final int playerGy = GeoEngine.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -135,32 +134,32 @@ public final class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = ge.getWorldX(gx); - final int y = ge.getWorldY(gy); - final int z = ge.getNearestZ(gx, gy, player.getZ()); + final int x = GeoEngine.getWorldX(gx); + final int y = GeoEngine.getWorldY(gy); + final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); + Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); 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, Cell.NSWE_EAST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); 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, Cell.NSWE_SOUTH); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); 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, Cell.NSWE_WEST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); 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); @@ -188,41 +187,41 @@ public final class GeoUtils { if (y > lastY) { - return Cell.NSWE_SOUTH_EAST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E; } else if (y < lastY) { - return Cell.NSWE_NORTH_EAST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E; } else { - return Cell.NSWE_EAST; + return GeoStructure.CELL_FLAG_E; } } else if (x < lastX) // west { if (y > lastY) { - return Cell.NSWE_SOUTH_WEST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W; } else if (y < lastY) { - return Cell.NSWE_NORTH_WEST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W; } else { - return Cell.NSWE_WEST; + return GeoStructure.CELL_FLAG_W; } } else // unchanged x { if (y > lastY) { - return Cell.NSWE_SOUTH; + return GeoStructure.CELL_FLAG_S; } else if (y < lastY) { - return Cell.NSWE_NORTH; + return GeoStructure.CELL_FLAG_N; } else { diff --git a/L2J_Mobius_2.5_Underground/dist/game/config/GeoEngine.ini b/L2J_Mobius_2.5_Underground/dist/game/config/GeoEngine.ini index 609e8a89a7..e740c678dd 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_2.5_Underground/dist/game/config/GeoEngine.ini @@ -6,33 +6,44 @@ # at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ 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 + # ================================================================= -# Path finding +# Pathfinding # ================================================================= # 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 +# an alternative path (e.g. walk around obstacle), default: True +PathFinding = True -# Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 -PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 +# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 -# Weight for nodes without obstacles far from walls -LowWeight = 0.5 +# Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 +MoveWeight = 10 +MoveWeightDiag = 14 -# Weight for nodes near walls -MediumWeight = 2 +# 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 -# Weight for nodes with obstacles -HighWeight = 3 +# 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. -# Default: LowWeight * sqrt(2) -DiagonalWeight = 0.707 +# Maximum number of generated nodes per one path-finding process, default 3500 +MaxIterations = 3500 # ================================================================= -# Other +# Line of Sight # ================================================================= -# Correct player Z after falling through the ground. -CorrectPlayerZ = False +# 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 diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/geodata/L2D_GeoEngine.patch b/L2J_Mobius_2.5_Underground/dist/game/data/geodata/L2D_GeoEngine.patch deleted file mode 100644 index f8577e3a0e..0000000000 --- a/L2J_Mobius_2.5_Underground/dist/game/data/geodata/L2D_GeoEngine.patch +++ /dev/null @@ -1,6035 +0,0 @@ -Index: dist/game/GeoDataConverter.bat -=================================================================== ---- dist/game/GeoDataConverter.bat (nonexistent) -+++ dist/game/GeoDataConverter.bat (working copy) -@@ -0,0 +1,6 @@ -+@echo off -+title L2D geodata converter -+ -+java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter -+ -+pause -Index: dist/game/GeoDataConverter.sh -=================================================================== ---- dist/game/GeoDataConverter.sh (nonexistent) -+++ dist/game/GeoDataConverter.sh (working copy) -@@ -0,0 +1,4 @@ -+#! /bin/sh -+ -+java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 -+ -Index: dist/game/config/GeoEngine.ini -=================================================================== ---- dist/game/config/GeoEngine.ini (revision 8409) -+++ dist/game/config/GeoEngine.ini (working copy) -@@ -1,6 +1,10 @@ - # ================================================================= - # Geodata - # ================================================================= -+# Because of real-time performance we are using geodata files only in -+# diagonal L2D format now (using filename e.g. 22_16.l2d). -+# L2D geodata can be obtained by conversion of existing L2J or L2OFF geodata. -+# Launch "GeoDataConverter.bat/sh" and follow instructions to start the conversion. - - # 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/ -@@ -13,6 +17,16 @@ - CoordSynchronize = 2 - - # ================================================================= -+# Path checking -+# ================================================================= -+ -+# 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 finding - # ================================================================= - -@@ -23,19 +37,23 @@ - # Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - --# Weight for nodes without obstacles far from walls --LowWeight = 0.5 -+# Base path weight, when moving from one node to another on axis direction, default: 10 -+BaseWeight = 10 - --# Weight for nodes near walls --MediumWeight = 2 -+# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -+DiagonalWeight = 14 - --# Weight for nodes with obstacles --HighWeight = 3 -+# When movement flags of target node is blocked to any direction, multiply movement weight by this multiplier. -+# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 10 -+ObstacleMultiplier = 10 - --# Weight for diagonal movement. --# Default: LowWeight * sqrt(2) --DiagonalWeight = 0.707 -+# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 20 -+# For proper function must be higher than BaseWeight and/or DiagonalWeight. -+HeuristicWeight = 20 - -+# Maximum number of generated nodes per one path-finding process, default 3500 -+MaxIterations = 3500 -+ - # ================================================================= - # Other - # ================================================================= -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (working copy) -@@ -55,9 +55,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -@@ -73,9 +72,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (working copy) -@@ -18,11 +18,11 @@ - - import java.util.List; - --import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -+import org.l2jmobius.gameserver.geoengine.GeoEngine; - import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -+import org.l2jmobius.gameserver.network.SystemMessageId; - import org.l2jmobius.gameserver.util.BuilderUtil; - - public class AdminPathNode implements IAdminCommandHandler -@@ -37,29 +37,30 @@ - { - if (command.equals("admin_path_find")) - { -- if (!Config.PATHFINDING) -- { -- BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); -- return true; -- } - if (activeChar.getTarget() != null) - { -- final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); -+ final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); - if (path == null) - { -- BuilderUtil.sendSysMessage(activeChar, "No Route!"); -- return true; -+ BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); - } -- for (AbstractNodeLoc a : path) -+ else - { -- BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); -+ for (Location point : path) -+ { -+ BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); -+ } - } - } - else - { -- BuilderUtil.sendSysMessage(activeChar, "No Target!"); -+ activeChar.sendPacket(SystemMessageId.INVALID_TARGET); - } - } -+ else -+ { -+ return false; -+ } - return true; - } - -Index: java/org/l2jmobius/Config.java -=================================================================== ---- java/org/l2jmobius/Config.java (revision 8409) -+++ java/org/l2jmobius/Config.java (working copy) -@@ -32,7 +32,6 @@ - import java.net.UnknownHostException; - import java.nio.charset.StandardCharsets; - import java.nio.file.Files; --import java.nio.file.Path; - import java.nio.file.Paths; - import java.time.Duration; - import java.util.ArrayList; -@@ -966,14 +965,17 @@ - // -------------------------------------------------- - // GeoEngine - // -------------------------------------------------- -- public static Path GEODATA_PATH; -+ public static String GEODATA_PATH; -+ public static int COORD_SYNCHRONIZE; -+ public static int PART_OF_CHARACTER_HEIGHT; -+ public static int MAX_OBSTACLE_HEIGHT; - public static boolean PATHFINDING; - public static String PATHFIND_BUFFERS; -- public static float LOW_WEIGHT; -- public static float MEDIUM_WEIGHT; -- public static float HIGH_WEIGHT; -- public static float DIAGONAL_WEIGHT; -- public static int COORD_SYNCHRONIZE; -+ public static int BASE_WEIGHT; -+ public static int DIAGONAL_WEIGHT; -+ public static int HEURISTIC_WEIGHT; -+ public static int OBSTACLE_MULTIPLIER; -+ public static int MAX_ITERATIONS; - public static boolean CORRECT_PLAYER_Z; - - /** Attribute System */ -@@ -2568,14 +2570,17 @@ - - // Load GeoEngine config file (if exists) - final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); -- GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); -+ GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); -+ COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); -+ MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); - PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -- LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); -- MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); -- HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); -- DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); -- COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); -+ DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); -+ OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); -+ HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); -+ MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); - CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); - - // Load AllowedPlayerRaces config file (if exists) -Index: java/org/l2jmobius/gameserver/geoengine/GeoEngine.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (revision 8435) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (working copy) -@@ -16,100 +16,90 @@ - */ - package org.l2jmobius.gameserver.geoengine; - --import java.io.IOException; -+import java.io.File; - import java.io.RandomAccessFile; - import java.nio.ByteOrder; --import java.nio.channels.FileChannel.MapMode; --import java.nio.file.Files; --import java.nio.file.Path; --import java.util.concurrent.atomic.AtomicReferenceArray; --import java.util.logging.Level; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.List; - import java.util.logging.Logger; - - import org.l2jmobius.Config; - import org.l2jmobius.gameserver.data.xml.DoorData; - import org.l2jmobius.gameserver.data.xml.FenceData; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; --import org.l2jmobius.gameserver.geoengine.geodata.IRegion; --import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; --import org.l2jmobius.gameserver.geoengine.geodata.Region; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.World; - import org.l2jmobius.gameserver.model.WorldObject; -+import org.l2jmobius.gameserver.model.actor.Creature; - import org.l2jmobius.gameserver.model.instancezone.Instance; --import org.l2jmobius.gameserver.model.interfaces.ILocational; --import org.l2jmobius.gameserver.util.GeoUtils; --import org.l2jmobius.gameserver.util.LinePointIterator; --import org.l2jmobius.gameserver.util.LinePointIterator3D; -+import org.l2jmobius.gameserver.util.MathUtil; - - /** -- * @author -Nemesiss-, HorridoJoho -+ * @author Hasha - */ - public class GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); -+ protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - -- private static final int WORLD_MIN_X = -655360; -- private static final int WORLD_MIN_Y = -589824; -- private static final int WORLD_MIN_Z = -16384; -+ private final ABlock[][] _blocks; -+ private final BlockNull _nullBlock; - -- /** 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; -- -- /** The regions array */ -- private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); -- -- private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; -- private static final int ELEVATED_SEE_OVER_DISTANCE = 2; -- private static final int MAX_SEE_OVER_HEIGHT = 48; -- -- protected GeoEngine() -+ /** -+ * GeoEngine constructor. Loads all geodata files of chosen geodata format. -+ */ -+ public GeoEngine() - { - LOGGER.info("GeoEngine: Initializing..."); -- for (int i = 0; i < _regions.length(); i++) -- { -- _regions.set(i, NullRegion.INSTANCE); -- } - -+ // initialize block container -+ _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; -+ -+ // load null block -+ _nullBlock = new BlockNull(); -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files according to geoengine config setup - int loaded = 0; -- try -+ long fileSize = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { -- for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { -- for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) -+ final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); -+ if (f.exists() && !f.isDirectory()) - { -- final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); -- if (Files.exists(geoFilePath)) -+ // region file is load-able, try to load it -+ if (loadGeoBlocks(rx, ry)) - { -- try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) -- { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -- loaded++; -- } -+ loaded++; -+ fileSize += f.length(); - } - } -+ else -+ { -+ // region file is not load-able, load null blocks -+ loadNullBlocks(rx, ry); -+ } - } - } -- catch (Exception e) -+ -+ LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -+ if (loaded > 0) - { -- LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); -- System.exit(1); -+ LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); - } - -- LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -- -- // Avoid wrong configs when no files are loaded. -+ // avoid wrong configs when no files are loaded - if (loaded == 0) - { - if (Config.PATHFINDING) -@@ -123,619 +113,820 @@ - LOGGER.info("GeoEngine: Forcing CoordSynchronize setting to -1."); - } - } -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); - } - - /** -- * @param geoX -- * @param geoY -- * @return the region -+ * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. -+ * @return boolean : True, when geodata file was loaded without problem. - */ -- private IRegion getRegion(int geoX, int geoY) -+ private final boolean loadGeoBlocks(int regionX, int regionY) - { -- final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); -- if ((region < 0) || (region >= _regions.length())) -+ final String filename = String.format(GeoFormat.L2D.getFilename(), regionX, regionY); -+ -+ // standard load -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) - { -- return null; -+ // initialize file buffer -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, GeoFormat.L2D); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ } -+ -+ // check data consistency -+ if (buffer.remaining() > 0) -+ { -+ LOGGER.warning("GeoEngine: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ } -+ -+ // loading was successful -+ return true; - } -- return _regions.get(region); -- } -- -- /** -- * @param filePath -- * @param regionX -- * @param regionY -- * @throws IOException -- */ -- 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")) -+ catch (Exception e) - { -- _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -+ // an error occured while loading, load null blocks -+ LOGGER.warning("GeoEngine: Error while loading " + filename + " region file."); -+ LOGGER.warning(e.getMessage()); -+ -+ // replace whole region file with null blocks -+ loadNullBlocks(regionX, regionY); -+ -+ // loading was not successful -+ return false; - } - } - - /** -- * @param regionX -- * @param regionY -+ * Loads null blocks. Used when no region file is detected or an error occurs during loading. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. - */ -- public void unloadRegion(int regionX, int regionY) -+ private final void loadNullBlocks(int regionX, int regionY) - { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); -- } -- -- /** -- * @param geoX -- * @param geoY -- * @return if geodata exist -- */ -- public boolean hasGeoPos(int geoX, int geoY) -- { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // load all null blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { -- return false; -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[blockX + ix][blockY + iy] = _nullBlock; -+ } - } -- return region.hasGeo(); - } - -+ // GEODATA - GENERAL -+ - /** -- * Checks the specified position for available geodata. -- * @param x the world x -- * @param y the world y -- * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise -+ * Converts world X to geodata X. -+ * @param worldX -+ * @return int : Geo X - */ -- public boolean hasGeo(int x, int y) -+ public static int getGeoX(int worldX) - { -- return hasGeoPos(getGeoX(x), getGeoY(y)); -+ return (MathUtil.limit(worldX, World.MAP_MIN_X, World.MAP_MAX_X) - World.MAP_MIN_X) >> 4; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe check -+ * Converts world Y to geodata Y. -+ * @param worldY -+ * @return int : Geo Y - */ -- public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) -+ public static int getGeoY(int worldY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return true; -- } -- return region.checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(worldY, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.MAP_MIN_Y) >> 4; - } - - /** -+ * Converts geodata X to world X. - * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe anti-corner cut -+ * @return int : World X - */ -- public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) -+ public static int getWorldX(int geoX) - { -- boolean can = true; -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- return can && checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(geoX, 0, GeoStructure.GEO_CELLS_X) << 4) + World.MAP_MIN_X + 8; - } - - /** -- * @param geoX -+ * Converts geodata Y to world Y. - * @param geoY -- * @param worldZ -- * @return the nearest Z value -+ * @return int : World Y - */ -- public int getNearestZ(int geoX, int geoY, int worldZ) -+ public static int getWorldY(int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return worldZ; -- } -- return region.getNearestZ(geoX, geoY, worldZ); -+ return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next lower Z value -+ * Returns block of geodata on given coordinates. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return {@link ABlock} : Block of geodata. - */ -- public int getNextLowerZ(int geoX, int geoY, int worldZ) -+ private final ABlock getBlock(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final int x = geoX / GeoStructure.BLOCK_CELLS_X; -+ final int y = geoY / GeoStructure.BLOCK_CELLS_Y; -+ -+ // if x or y is out of array return null -+ if ((x > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) - { -- return worldZ; -+ return _blocks[x][y]; - } -- return region.getNextLowerZ(geoX, geoY, worldZ); -+ return null; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next higher Z value -+ * Check if geo coordinates has geo. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return boolean : True, if given geo coordinates have geodata - */ -- public int getNextHigherZ(int geoX, int geoY, int worldZ) -+ public boolean hasGeoPos(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final ABlock block = getBlock(geoX, geoY); -+ if (block == null) // null block check - { -- return worldZ; -+ // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) -+ // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); -+ return false; - } -- return region.getNextHigherZ(geoX, geoY, worldZ); -+ return block.hasGeoPos(); - } - - /** -- * Gets the Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the height of cell, which is closest to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, closest to given coordinates. - */ -- public int getHeight(int x, int y, int z) -+ public short getHeightNearest(int geoX, int geoY, int worldZ) - { -- return getNearestZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getHeightNearest(geoX, geoY, worldZ) : (short) worldZ; - } - - /** -- * Gets the next lower Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the NSWE flag byte of cell, which is closes to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. - */ -- public int getLowerHeight(int x, int y, int z) -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) - { -- return getNextLowerZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getNsweNearest(geoX, geoY, worldZ) : (byte) 0xFF; - } - - /** -- * Gets the next higher Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Check if world coordinates has geo. -+ * @param worldX : World X -+ * @param worldY : World Y -+ * @return boolean : True, if given world coordinates have geodata - */ -- public int getHigherHeight(int x, int y, int z) -+ public boolean hasGeo(int worldX, int worldY) - { -- return getNextHigherZ(getGeoX(x), getGeoY(y), z); -+ return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); - } - - /** -- * @param worldX -- * @return the geo X -+ * Returns closest Z coordinate according to geodata. -+ * @param worldX : world x -+ * @param worldY : world y -+ * @param worldZ : world z -+ * @return short : nearest Z coordinates according to geodata - */ -- public int getGeoX(int worldX) -+ public short getHeight(int worldX, int worldY, int worldZ) - { -- return (worldX - WORLD_MIN_X) / 16; -+ return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - -- /** -- * @param worldY -- * @return the geo Y -- */ -- public int getGeoY(int worldY) -- { -- return (worldY - WORLD_MIN_Y) / 16; -- } -+ // PATHFINDING - - /** -- * @param worldZ -- * @return the geo Z -+ * Check line of sight from {@link WorldObject} to {@link WorldObject}. -+ * @param origin : The origin object. -+ * @param target : The target object. -+ * @return {@code boolean} : True if origin can see target - */ -- public int getGeoZ(int worldZ) -+ public boolean canSeeTarget(WorldObject origin, WorldObject target) - { -- return (worldZ - WORLD_MIN_Z) / 16; -- } -- -- /** -- * @param geoX -- * @return the world X -- */ -- public int getWorldX(int geoX) -- { -- return (geoX * 16) + WORLD_MIN_X + 8; -- } -- -- /** -- * @param geoY -- * @return the world Y -- */ -- public int getWorldY(int geoY) -- { -- return (geoY * 16) + WORLD_MIN_Y + 8; -- } -- -- /** -- * @param geoZ -- * @return the world Z -- */ -- public int getWorldZ(int geoZ) -- { -- return (geoZ * 16) + WORLD_MIN_Z + 8; -- } -- -- /** -- * Can see target. Doors as target always return true. Checks doors between. -- * @param cha the character -- * @param target the target -- * @return {@code true} if the character can see the target (LOS), {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, WorldObject target) -- { -- if (target.isDoor()) -+ if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) - { -- // Can always see doors. - return true; - } -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param cha the character -- * @param worldPosition the world position -- * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) -- { -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param world -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @param tworld the target's instanceId -- * @return -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) -- { -- return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instance -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) -- { -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) -+ -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = target.getX(); -+ final int ty = target.getY(); -+ final int tz = target.getZ(); -+ -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } -- return canSeeTarget(x, y, z, tx, ty, tz); -- } -- -- /** -- * @param prevX -- * @param prevY -- * @param prevGeoZ -- * @param curX -- * @param curY -- * @param nswe -- * @return the LOS Z value -- */ -- private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) -- { -- if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { -- throw new RuntimeException("Multiple directions!"); -+ return false; - } - -- if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- return getNearestZ(curX, curY, prevGeoZ); -+ return true; - } - -- return getNextHigherZ(curX, curY, prevGeoZ); -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) -+ { -+ return true; -+ } -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) -+ { -+ return goz == gtz; -+ } -+ -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) -+ { -+ oheight = ((Creature) origin).getCollisionHeight() * 2; -+ } -+ -+ double theight = 0; -+ if (target.isCreature()) -+ { -+ theight = ((Creature) target).getCollisionHeight() * 2; -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); - } - - /** -- * Can see target. Does not check doors between. -- * @param xValue the x coordinate -- * @param yValue the y coordinate -- * @param zValue the z coordinate -- * @param txValue the target's x coordinate -- * @param tyValue the target's y coordinate -- * @param tzValue the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -+ * Check line of sight from {@link WorldObject} to {@link Location}. -+ * @param origin : The origin object. -+ * @param position : The target position. -+ * @return {@code boolean} : True if object can see position - */ -- public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) -+ public boolean canSeeTarget(WorldObject origin, Location position) - { -- int x = xValue; -- int y = yValue; -- int tx = txValue; -- int ty = tyValue; -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = position.getX(); -+ final int ty = position.getY(); -+ final int tz = position.getZ(); - -- int geoX = getGeoX(x); -- int geoY = getGeoY(y); -- int tGeoX = getGeoX(tx); -- int tGeoY = getGeoY(ty); -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) -+ { -+ return false; -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) -+ { -+ return false; -+ } - -- int z = getNearestZ(geoX, geoY, zValue); -- int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- // Fastpath. -- if ((geoX == tGeoX) && (geoY == tGeoY)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- if (hasGeoPos(tGeoX, tGeoY)) -- { -- return z == tz; -- } - return true; - } - -- if (tz > z) -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) - { -- int tmp = tx; -- tx = x; -- x = tmp; -- -- tmp = ty; -- ty = y; -- y = tmp; -- -- tmp = tz; -- tz = z; -- z = tmp; -- -- tmp = tGeoX; -- tGeoX = geoX; -- geoX = tmp; -- -- tmp = tGeoY; -- tGeoY = geoY; -- geoY = tmp; -+ return goz == gtz; - } - -- final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); -- // First point is guaranteed to be available, skip it, we can always see our own position. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- final int prevZ = pointIter.z(); -- int prevGeoZ = prevZ; -- int ptIndex = 0; -- while (pointIter.next()) -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight(); -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); -+ } -+ -+ /** -+ * Simple check for origin to target visibility. -+ * @param goxValue : origin X geodata coordinate -+ * @param goyValue : origin Y geodata coordinate -+ * @param gozValue : origin Z geodata coordinate -+ * @param oheight : origin height (if instance of {@link Character}) -+ * @param gtxValue : target X geodata coordinate -+ * @param gtyValue : target Y geodata coordinate -+ * @param gtzValue : target Z geodata coordinate -+ * @param theight : target height (if instance of {@link Character}) -+ * @param instance -+ * @return {@code boolean} : True, when target can be seen. -+ */ -+ private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) -+ { -+ int goz = gozValue; -+ int gtz = gtzValue; -+ int gox = goxValue; -+ int goy = goyValue; -+ int gtx = gtxValue; -+ int gty = gtyValue; -+ -+ // get line of sight Z coordinates -+ double losoz = goz + ((oheight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ double lostz = gtz + ((theight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ -+ // get X delta and signum -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirox = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; -+ final byte dirtx = sx > 0 ? GeoStructure.CELL_FLAG_W : GeoStructure.CELL_FLAG_E; -+ -+ // get Y delta and signum -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte diroy = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ final byte dirty = sy > 0 ? GeoStructure.CELL_FLAG_N : GeoStructure.CELL_FLAG_S; -+ -+ // get Z delta -+ final int dm = Math.max(dx, dy); -+ final double dz = (lostz - losoz) / dm; -+ -+ // get direction flag for diagonal movement -+ final byte diroxy = getDirXY(dirox, diroy); -+ final byte dirtxy = getDirXY(dirtx, dirty); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte diro; -+ byte dirt; -+ -+ // initialize node values -+ int nox = gox; -+ int noy = goy; -+ int ntx = gtx; -+ int nty = gty; -+ byte nsweo = getNsweNearest(gox, goy, goz); -+ byte nswet = getNsweNearest(gtx, gty, gtz); -+ -+ // loop -+ ABlock block; -+ int index; -+ for (int i = 0; i < ((dm + 1) / 2); i++) -+ { -+ // reset direction flag -+ diro = 0; -+ dirt = 0; - -- if ((curX == prevX) && (curY == prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- continue; -+ // calculate next point XY coordinates -+ d -= dy; -+ d += dx; -+ nox += sx; -+ ntx -= sx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroxy; -+ dirt |= dirtxy; - } -+ else if (e2 > -dy) -+ { -+ // calculate next point X coordinate -+ d -= dy; -+ nox += sx; -+ ntx -= sx; -+ diro |= dirox; -+ dirt |= dirtx; -+ } -+ else if (e2 < dx) -+ { -+ // calculate next point Y coordinate -+ d += dx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroy; -+ dirt |= dirty; -+ } - -- final int beeCurZ = pointIter.z(); -- int curGeoZ = prevGeoZ; -- -- // Check if the position has geodata. -- if (hasGeoPos(curX, curY)) - { -- final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); -- curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); -- int maxHeight; -- if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) -+ // get block of the next cell -+ block = getBlock(nox, noy); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nsweo & diro) == 0) - { -- maxHeight = z + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); - } - else - { -- maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); - } - -- boolean canSeeThrough = false; -- if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) -+ // layer does not exist, return -+ if (index == -1) - { -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else -- { -- canSeeThrough = true; -- } -+ return false; - } - -- if (!canSeeThrough) -+ // get layer and next line of sight Z coordinate -+ goz = block.getHeight(index); -+ losoz += dz; -+ -+ // perform line of sight check, return when fails -+ if ((goz - losoz) > Config.MAX_OBSTACLE_HEIGHT) - { - return false; - } -+ -+ // get layer nswe -+ nsweo = block.getNswe(index); - } -+ { -+ // get block of the next cell -+ block = getBlock(ntx, nty); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nswet & dirt) == 0) -+ { -+ index = block.getIndexAbove(ntx, nty, gtz - GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ else -+ { -+ index = block.getIndexBelow(ntx, nty, gtz + GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ -+ // layer does not exist, return -+ if (index == -1) -+ { -+ return false; -+ } -+ -+ // get layer and next line of sight Z coordinate -+ gtz = block.getHeight(index); -+ lostz -= dz; -+ -+ // perform line of sight check, return when fails -+ if ((gtz - lostz) > Config.MAX_OBSTACLE_HEIGHT) -+ { -+ return false; -+ } -+ -+ // get layer nswe -+ nswet = block.getNswe(index); -+ } - -- prevX = curX; -- prevY = curY; -- prevGeoZ = curGeoZ; -- ++ptIndex; -+ // update coords -+ gox = nox; -+ goy = noy; -+ gtx = ntx; -+ gty = nty; - } - -- return true; -+ // when iteration is completed, compare final Z coordinates -+ return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); - } - - /** -- * Move check. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param zValue the z coordinate -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tzValue the target's z coordinate -- * @param instance the instance -- * @return the last Location (x,y,z) where player can walk - just before wall -+ * Check movement from coordinates to coordinates. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {code boolean} : True if target coordinates are reachable from origin coordinates - */ -- public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) -+ public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- final int geoX = getGeoX(x); -- final int geoY = getGeoY(y); -- final int z = getNearestZ(geoX, geoY, zValue); -- final int tGeoX = getGeoX(tx); -- final int tGeoY = getGeoY(ty); -- final int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } -- if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = z; -+ // perform geodata check -+ final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); -+ return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); -+ } -+ -+ /** -+ * Check movement from origin to target. Returns last available point in the checked path. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {@link Location} : Last point where object can walk (just before wall) -+ */ -+ public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) -+ { -+ return new Location(ox, oy, oz); -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) -+ { -+ return new Location(ox, oy, oz); -+ } - -- while (pointIter.next()) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -- -- if (hasGeoPos(prevX, prevY)) -- { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -- { -- // Can't move, return previous location. -- return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); -- } -- } -- -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return new Location(tx, ty, tz); - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != tz)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- // Different floors, return start location. -- return new Location(x, y, z); -+ return new Location(tx, ty, tz); - } - -- return new Location(tx, ty, tz); -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) -+ { -+ return new Location(tx, ty, tz); -+ } -+ -+ // perform geodata check -+ return checkMove(gox, goy, goz, gtx, gty, gtz, instance); - } - - /** -- * Checks if its possible to move from one location to another. -- * @param fromX the X coordinate to start checking from -- * @param fromY the Y coordinate to start checking from -- * @param fromZvalue the Z coordinate to start checking from -- * @param toX the X coordinate to end checking at -- * @param toY the Y coordinate to end checking at -- * @param toZvalue the Z coordinate to end checking at -- * @param instance the instance -- * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise -+ * With this method you can check if a position is visible or can be reached by beeline movement.
-+ * Target X and Y reachable and Z is on same floor: -+ *
    -+ *
  • Location of the target with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y reachable but Z is on another floor: -+ *
    -+ *
  • Location of the origin with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y not reachable: -+ *
    -+ *
  • Last accessible location in destination to target.
  • -+ *
-+ * @param gox : origin X geodata coordinate -+ * @param goy : origin Y geodata coordinate -+ * @param goz : origin Z geodata coordinate -+ * @param gtx : target X geodata coordinate -+ * @param gty : target Y geodata coordinate -+ * @param gtz : target Z geodata coordinate -+ * @param instance -+ * @return {@link GeoLocation} : The last allowed point of movement. - */ -- public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) -+ protected final GeoLocation checkMove(int gox, int goy, int goz, int gtx, int gty, int gtz, Instance instance) - { -- final int geoX = getGeoX(fromX); -- final int geoY = getGeoY(fromY); -- final int fromZ = getNearestZ(geoX, geoY, fromZvalue); -- final int tGeoX = getGeoX(toX); -- final int tGeoY = getGeoY(toY); -- final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); -- -- if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) -+ if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } -- if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) -+ if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = fromZ; -+ // get X delta, signum and direction flag -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirX = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; - -- while (pointIter.next()) -+ // get Y delta, signum and direction flag -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte dirY = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ -+ // get direction flag for diagonal movement -+ final byte dirXY = getDirXY(dirX, dirY); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte direction; -+ -+ // load pointer coordinates -+ int gpx = gox; -+ int gpy = goy; -+ int gpz = goz; -+ -+ // load next pointer -+ int nx = gpx; -+ int ny = gpy; -+ -+ // loop -+ int count = 0; -+ while (count++ < Config.MAX_ITERATIONS) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -+ direction = 0; - -- if (hasGeoPos(prevX, prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -+ d -= dy; -+ d += dx; -+ nx += sx; -+ ny += sy; -+ direction |= dirXY; -+ } -+ else if (e2 > -dy) -+ { -+ d -= dy; -+ nx += sx; -+ direction |= dirX; -+ } -+ else if (e2 < dx) -+ { -+ d += dx; -+ ny += sy; -+ direction |= dirY; -+ } -+ -+ // obstacle found, return -+ if ((getNsweNearest(gpx, gpy, gpz) & direction) == 0) -+ { -+ return new GeoLocation(gpx, gpy, gpz); -+ } -+ -+ // update pointer coordinates -+ gpx = nx; -+ gpy = ny; -+ gpz = getHeightNearest(nx, ny, gpz); -+ -+ // target coordinates reached -+ if ((gpx == gtx) && (gpy == gty)) -+ { -+ if (gpz == gtz) - { -- return false; -+ // path found, Z coordinates are okay, return target point -+ return new GeoLocation(gtx, gty, gtz); - } -+ -+ // path found, Z coordinates are not okay, return last good point -+ return new GeoLocation(gpx, gpy, gpz); - } -+ } -+ -+ return new GeoLocation(gox, goy, goz); -+ } -+ -+ /** -+ * Returns diagonal NSWE flag format of combined two NSWE flags. -+ * @param dirX : X direction NSWE flag -+ * @param dirY : Y direction NSWE flag -+ * @return byte : NSWE flag of combined direction -+ */ -+ private static byte getDirXY(byte dirX, byte dirY) -+ { -+ // check axis directions -+ if (dirY == GeoStructure.CELL_FLAG_N) -+ { -+ if (dirX == GeoStructure.CELL_FLAG_W) -+ { -+ return GeoStructure.CELL_FLAG_NW; -+ } - -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return GeoStructure.CELL_FLAG_NE; - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) -+ if (dirX == GeoStructure.CELL_FLAG_W) - { -- // Different floors. -- return false; -+ return GeoStructure.CELL_FLAG_SW; - } - -- return true; -+ return GeoStructure.CELL_FLAG_SE; - } - -+ /** -+ * Returns the list of location objects as a result of complete path calculation. -+ * @param ox : origin x -+ * @param oy : origin y -+ * @param oz : origin z -+ * @param tx : target x -+ * @param ty : target y -+ * @param tz : target z -+ * @param instance -+ * @return {@code List} : complete path from nodes -+ */ -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ return null; -+ } -+ -+ /** -+ * Returns the instance of the {@link GeoEngine}. -+ * @return {@link GeoEngine} : The instance. -+ */ - public static GeoEngine getInstance() - { - return SingletonHolder.INSTANCE; -@@ -743,6 +934,6 @@ - - private static class SingletonHolder - { -- protected static final GeoEngine INSTANCE = new GeoEngine(); -+ protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); - } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (working copy) -@@ -20,88 +20,84 @@ - import java.util.LinkedList; - import java.util.List; - import java.util.ListIterator; --import java.util.logging.Level; --import java.util.logging.Logger; - - import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; --import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; --import org.l2jmobius.gameserver.model.World; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -+import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.instancezone.Instance; - - /** -- * @author -Nemesiss- -+ * @author Hasha - */ --public class GeoEnginePathfinding -+final class GeoEnginePathfinding extends GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); -+ // pre-allocated buffers -+ private final BufferHolder[] _buffers; - -- private BufferInfo[] _buffers; -- - protected GeoEnginePathfinding() - { -- try -+ super(); -+ -+ final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ _buffers = new BufferHolder[array.length]; -+ int count = 0; -+ for (int i = 0; i < array.length; i++) - { -- final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ final String buf = array[i]; -+ final String[] args = buf.split("x"); - -- _buffers = new BufferInfo[array.length]; -- -- String buf; -- String[] args; -- for (int i = 0; i < array.length; i++) -+ try - { -- buf = array[i]; -- args = buf.split("x"); -- if (args.length != 2) -- { -- throw new Exception("Invalid buffer definition: " + buf); -- } -- -- _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); -+ final int size = Integer.parseInt(args[1]); -+ count += size; -+ _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); - } -+ catch (Exception e) -+ { -+ LOGGER.warning("GeoEnginePathfinding: Can not load buffer setting: " + buf); -+ } - } -- catch (Exception e) -- { -- LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); -- throw new Error("CellPathFinding: load aborted"); -- } -+ -+ LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); - } - -- public boolean pathNodesExist(short regionoffset) -+ @Override -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- return false; -- } -- -- public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) -- { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -- if (!GeoEngine.getInstance().hasGeo(x, y)) -+ // get origin and check existing geo coords -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { - 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)) -+ -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coords -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { - 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)))); -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // Prepare buffer for pathfinding calculations -+ final NodeBuffer buffer = getBuffer(64 + (2 * Math.max(Math.abs(gox - gtx), Math.abs(goy - gty)))); - if (buffer == null) - { - return null; - } - -- List path = null; -+ // find path -+ List path = null; - try - { -- final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); -- -+ final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); - if (result == null) - { - return null; -@@ -125,33 +121,45 @@ - return path; - } - -- int currentX, currentY, currentZ; -- ListIterator middlePoint; -+ // get path list iterator -+ final ListIterator point = path.listIterator(); - -- middlePoint = path.listIterator(); -- currentX = x; -- currentY = y; -- currentZ = z; -+ // get node A (origin) -+ int nodeAx = gox; -+ int nodeAy = goy; -+ short nodeAz = goz; - -- while (middlePoint.hasNext()) -+ // get node B -+ GeoLocation nodeB = (GeoLocation) point.next(); -+ -+ // iterate thought the path to optimize it -+ int count = 0; -+ while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) - { -- final AbstractNodeLoc locMiddle = middlePoint.next(); -- if (!middlePoint.hasNext()) -- { -- break; -- } -+ // get node C -+ final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - -- final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); -- if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) -+ // check movement from node A to node C -+ final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); -+ if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) - { -- middlePoint.remove(); -+ // can move from node A to node C -+ -+ // remove node B -+ point.remove(); - } - else - { -- currentX = locMiddle.getX(); -- currentY = locMiddle.getY(); -- currentZ = locMiddle.getZ(); -+ // can not move from node A to node C -+ -+ // set node A (node B is part of path, update A coordinates) -+ nodeAx = nodeB.getGeoX(); -+ nodeAy = nodeB.getGeoY(); -+ nodeAz = (short) nodeB.getZ(); - } -+ -+ // set node B -+ nodeB = (GeoLocation) point.next(); - } - - return path; -@@ -158,144 +166,102 @@ - } - - /** -- * Convert geodata position to pathnode position -- * @param geo_pos -- * @return pathnode position -+ * Create list of node locations as result of calculated buffer node tree. -+ * @param node : the entry point -+ * @return List : list of node location - */ -- public short getNodePos(int geo_pos) -+ private static List constructPath(Node node) - { -- return (short) (geo_pos >> 3); // OK? -- } -- -- /** -- * Convert node position to pathnode block position -- * @param node_pos -- * @return pathnode block position (0...255) -- */ -- public short getNodeBlock(int node_pos) -- { -- return (short) (node_pos % 256); -- } -- -- public byte getRegionX(int node_pos) -- { -- return (byte) ((node_pos >> 8) + World.TILE_X_MIN); -- } -- -- public byte getRegionY(int node_pos) -- { -- return (byte) ((node_pos >> 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 node_x rx -- * @return -- */ -- public int calculateWorldX(short node_x) -- { -- return World.MAP_MIN_X + (node_x * 128) + 48; -- } -- -- /** -- * Convert pathnode y to World y position -- * @param node_y -- * @return -- */ -- public int calculateWorldY(short node_y) -- { -- return World.MAP_MIN_Y + (node_y * 128) + 48; -- } -- -- private List constructPath(AbstractNode nodeValue) -- { -- final LinkedList path = new LinkedList<>(); -- int previousDirectionX = Integer.MIN_VALUE; -- int previousDirectionY = Integer.MIN_VALUE; -- int directionX, directionY; -+ // create empty list -+ final LinkedList list = new LinkedList<>(); - -- AbstractNode node = nodeValue; -- while (node.getParent() != null) -+ // set direction X/Y -+ int dx = 0; -+ int dy = 0; -+ -+ // get target parent -+ Node target = node; -+ Node parent = target.getParent(); -+ -+ // while parent exists -+ int count = 0; -+ while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { -- directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); -- directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); -+ // get parent <> target direction X/Y -+ final int nx = parent.getLoc().getGeoX() - target.getLoc().getGeoX(); -+ final int ny = parent.getLoc().getGeoY() - target.getLoc().getGeoY(); - -- // only add a new route point if moving direction changes -- if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) -+ // direction has changed? -+ if ((dx != nx) || (dy != ny)) - { -- previousDirectionX = directionX; -- previousDirectionY = directionY; -+ // add node to the beginning of the list -+ list.addFirst(target.getLoc()); - -- path.addFirst(node.getLoc()); -- node.setLoc(null); -+ // update direction X/Y -+ dx = nx; -+ dy = ny; - } - -- node = node.getParent(); -+ // move to next node, set target and get its parent -+ target = parent; -+ parent = target.getParent(); - } - -- return path; -+ // return list -+ return list; - } - -- private CellNodeBuffer alloc(int size) -+ /** -+ * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer. -+ * @param size : pre-calculated minimal required size -+ * @return NodeBuffer : buffer -+ */ -+ private final NodeBuffer getBuffer(int size) - { -- CellNodeBuffer current = null; -- for (BufferInfo i : _buffers) -+ NodeBuffer current = null; -+ for (BufferHolder holder : _buffers) - { -- if (i.mapSize >= size) -+ // Find proper size of buffer -+ if (holder._size < size) - { -- for (CellNodeBuffer buf : i.buffer) -+ continue; -+ } -+ -+ // Find unlocked NodeBuffer -+ for (NodeBuffer buffer : holder._buffer) -+ { -+ if (!buffer.isLocked()) - { -- if (buf.lock()) -- { -- current = buf; -- break; -- } -+ continue; - } -- if (current != null) -- { -- break; -- } - -- // not found, allocate temporary buffer -- current = new CellNodeBuffer(i.mapSize); -- current.lock(); -- if (i.buffer.size() < i.count) -- { -- i.buffer.add(current); -- break; -- } -+ return buffer; - } -+ -+ // NodeBuffer not found, allocate temporary buffer -+ current = new NodeBuffer(holder._size); -+ current.isLocked(); - } - - return current; - } - -- private static final class BufferInfo -+ /** -+ * NodeBuffer container with specified size and count of separate buffers. -+ */ -+ private static final class BufferHolder - { -- final int mapSize; -- final int count; -- ArrayList buffer; -+ final int _size; -+ List _buffer; - -- public BufferInfo(int size, int cnt) -+ public BufferHolder(int size, int count) - { -- mapSize = size; -- count = cnt; -- buffer = new ArrayList<>(count); -+ _size = size; -+ _buffer = new ArrayList<>(count); -+ for (int i = 0; i < count; i++) -+ { -+ _buffer.add(new NodeBuffer(size)); -+ } - } - } -- -- public static GeoEnginePathfinding getInstance() -- { -- return SingletonHolder.INSTANCE; -- } -- -- private static class SingletonHolder -- { -- protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); -- } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (working copy) -@@ -0,0 +1,197 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @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 height of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getHeightNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first above given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, above given coordinates. -+ */ -+ public abstract short getHeightAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first below given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is closest to given coordinates. -+ * @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 the NSWE flag byte of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getNsweNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first above given coordinates. -+ * @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 getNsweAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first below given coordinates. -+ * @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 getNsweBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is closes layer to given coordinates. -+ * @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. -+ * @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 above given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is first layer below given coordinates. -+ * @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 index to data of the cell, which is first layer below given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexBelowOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeight(int index); -+ -+ /** -+ * Returns the height of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightOriginal(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNswe(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNsweOriginal(int index); -+ -+ /** -+ * Sets the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @param nswe : New NSWE flag byte. -+ */ -+ public abstract void setNswe(int index, byte nswe); -+ -+ /** -+ * Saves the block in L2D format to {@link BufferedOutputStream}. Used only for L2D geodata conversion. -+ * @param stream : The stream. -+ * @throws IOException : Can't save the block to steam. -+ */ -+ public abstract void saveBlock(BufferedOutputStream stream) throws IOException; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (working copy) -@@ -0,0 +1,252 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockComplex(ByteBuffer bb, GeoFormat format) -+ { -+ // initialize buffer -+ _buffer = new byte[GeoStructure.BLOCK_CELLS * 3]; -+ -+ // load data -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // 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); -+ } -+ else -+ { -+ // get nswe -+ final byte nswe = bb.get(); -+ _buffer[i * 3] = nswe; -+ -+ // get height -+ final short height = bb.getShort(); -+ _buffer[(i * 3) + 1] = (byte) (height & 0x00FF); -+ _buffer[(i * 3) + 2] = (byte) (height >> 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height > worldZ ? height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height < worldZ ? height : Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public 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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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 int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_COMPLEX_L2D); -+ -+ // write block data -+ stream.write(_buffer, 0, GeoStructure.BLOCK_CELLS * 3); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (working copy) -@@ -0,0 +1,176 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockFlat extends ABlock -+{ -+ protected final short _height; -+ protected byte _nswe; -+ -+ /** -+ * Creates FlatBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockFlat(ByteBuffer bb, GeoFormat format) -+ { -+ _height = bb.getShort(); -+ _nswe = format != GeoFormat.L2D ? 0x0F : (byte) (0xFF); -+ if (format == GeoFormat.L2OFF) -+ { -+ bb.getShort(); -+ } -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return true; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height > worldZ ? _height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height < worldZ ? _height : Short.MAX_VALUE; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height > worldZ ? _nswe : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height < worldZ ? _nswe : 0; -+ } -+ -+ @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return index -+ return _height < worldZ ? 0 : -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _nswe = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_FLAT_L2D); -+ -+ // write height -+ stream.write((byte) (_height & 0x00FF)); -+ stream.write((byte) (_height >> 8)); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (working copy) -@@ -0,0 +1,465 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+import java.nio.ByteOrder; -+import java.util.Arrays; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockMultilayer extends ABlock -+{ -+ private static final int MAX_LAYERS = Byte.MAX_VALUE; -+ -+ private static ByteBuffer _temp; -+ -+ /** -+ * Initializes the temporarily buffer. -+ */ -+ public static void initialize() -+ { -+ // initialize temporarily buffer and sorting mechanism -+ _temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3); -+ _temp.order(ByteOrder.LITTLE_ENDIAN); -+ } -+ -+ /** -+ * Releases temporarily buffer. -+ */ -+ public static void release() -+ { -+ _temp = null; -+ } -+ -+ protected byte[] _buffer; -+ -+ /** -+ * Implicit constructor for children class. -+ */ -+ protected BlockMultilayer() -+ { -+ _buffer = null; -+ } -+ -+ /** -+ * Creates MultilayerBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockMultilayer(ByteBuffer bb, GeoFormat format) -+ { -+ // 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 = format != GeoFormat.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++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // get data -+ final short data = bb.getShort(); -+ -+ // add nswe and height -+ _temp.put((byte) (data & 0x000F)); -+ _temp.putShort((short) ((short) (data & 0xFFF0) >> 1)); -+ } -+ else -+ { -+ // add nswe -+ _temp.put(bb.get()); -+ -+ // add height -+ _temp.putShort(bb.getShort()); -+ } -+ } -+ } -+ -+ // 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 height -+ if (height > worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, return minimum value -+ return Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 height -+ if (height < worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, return maximum value -+ return Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 nswe -+ if (height > worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 nswe -+ if (height < worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @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 first layer data (first from bottom) -+ byte layers = _buffer[index++]; -+ -+ // loop though all cell layers, find closest layer -+ 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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ // get height -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(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]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ // get nswe -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ // set nswe -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_MULTILAYER_L2D); -+ -+ // for each cell -+ int index = 0; -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ // write layers count -+ final byte layers = _buffer[index++]; -+ stream.write(layers); -+ -+ // write cell data -+ stream.write(_buffer, index, layers * 3); -+ -+ // move index to next cell -+ index += layers * 3; -+ } -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (working copy) -@@ -0,0 +1,150 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockNull extends ABlock -+{ -+ private final byte _nswe; -+ -+ public BlockNull() -+ { -+ _nswe = (byte) 0xFF; -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return false; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweBelow(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) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) -+ { -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (nonexistent) -@@ -1,48 +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 . -- */ --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() -- { -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (nonexistent) -@@ -1,77 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (nonexistent) -@@ -1,56 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (working copy) -@@ -0,0 +1,39 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public enum GeoFormat -+{ -+ L2J("%d_%d.l2j"), -+ L2OFF("%d_%d_conv.dat"), -+ L2D("%d_%d.l2d"); -+ -+ private final String _filename; -+ -+ private GeoFormat(String filename) -+ { -+ _filename = filename; -+ } -+ -+ public String getFilename() -+ { -+ return _filename; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (working copy) -@@ -0,0 +1,67 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.geoengine.GeoEngine; -+import org.l2jmobius.gameserver.model.Location; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoLocation extends Location -+{ -+ private byte _nswe; -+ -+ public GeoLocation(int x, int y, int z) -+ { -+ super(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public void set(int x, int y, short z) -+ { -+ super.setXYZ(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public int getGeoX() -+ { -+ return _x; -+ } -+ -+ public int getGeoY() -+ { -+ return _y; -+ } -+ -+ @Override -+ public int getX() -+ { -+ return GeoEngine.getWorldX(_x); -+ } -+ -+ @Override -+ public int getY() -+ { -+ return GeoEngine.getWorldY(_y); -+ } -+ -+ public byte getNSWE() -+ { -+ return _nswe; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (working copy) -@@ -0,0 +1,70 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoStructure -+{ -+ // cells -+ public static final byte CELL_FLAG_E = 1 << 0; -+ public static final byte CELL_FLAG_W = 1 << 1; -+ public static final byte CELL_FLAG_S = 1 << 2; -+ public static final byte CELL_FLAG_N = 1 << 3; -+ public static final byte CELL_FLAG_SE = 1 << 4; -+ public static final byte CELL_FLAG_SW = 1 << 5; -+ public static final byte CELL_FLAG_NE = 1 << 6; -+ public static final byte CELL_FLAG_NW = (byte) (1 << 7); -+ -+ public static final int CELL_HEIGHT = 8; -+ public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6; -+ -+ // blocks -+ public static final byte TYPE_FLAT_L2J_L2OFF = 0; -+ public static final byte TYPE_FLAT_L2D = (byte) 0xD0; -+ public static final byte TYPE_COMPLEX_L2J = 1; -+ public static final byte TYPE_COMPLEX_L2OFF = 0x40; -+ public static final byte TYPE_COMPLEX_L2D = (byte) 0xD1; -+ 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) -+ public static final byte TYPE_MULTILAYER_L2D = (byte) 0xD2; -+ -+ 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; -+ -+ // regions -+ 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; -+ -+ // global geodata -+ private static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); -+ private 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; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (nonexistent) -@@ -1,42 +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 . -- */ --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); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (working copy) -@@ -0,0 +1,53 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public interface IGeoObject -+{ -+ /** -+ * Returns geodata X coordinate of the {@link IGeoObject}. -+ * @return int : Geodata X coordinate. -+ */ -+ int getGeoX(); -+ -+ /** -+ * Returns geodata Y coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Y coordinate. -+ */ -+ int getGeoY(); -+ -+ /** -+ * Returns geodata Z coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Z coordinate. -+ */ -+ int getGeoZ(); -+ -+ /** -+ * Returns height of the {@link IGeoObject}. -+ * @return int : Height. -+ */ -+ int getHeight(); -+ -+ /** -+ * Returns {@link IGeoObject} data. -+ * @return byte[][] : {@link IGeoObject} data. -+ */ -+ byte[][] getObjectGeoData(); -+} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (nonexistent) -@@ -1,47 +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 . -- */ --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(); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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); -- -- // 1 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (nonexistent) -@@ -1,55 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --/** -- * @author HorridoJoho -- */ --public final class NullRegion implements IRegion --{ -- public static final NullRegion INSTANCE = new NullRegion(); -- -- @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 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Region.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (nonexistent) -@@ -1,92 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (nonexistent) -@@ -1,87 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public abstract class AbstractNode --{ -- private T _loc; -- private AbstractNode _parent; -- -- public AbstractNode(T loc) -- { -- _loc = loc; -- } -- -- public void setParent(AbstractNode p) -- { -- _parent = p; -- } -- -- public AbstractNode getParent() -- { -- return _parent; -- } -- -- public T getLoc() -- { -- return _loc; -- } -- -- public void setLoc(T l) -- { -- _loc = l; -- } -- -- @Override -- public int hashCode() -- { -- final int prime = 31; -- int result = 1; -- result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); -- return result; -- } -- -- @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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (nonexistent) -@@ -1,33 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --/** -- * @author -Nemesiss- -- */ --public abstract class AbstractNodeLoc --{ -- public abstract int getX(); -- -- public abstract int getY(); -- -- public abstract int getZ(); -- -- public abstract int getNodeX(); -- -- public abstract int getNodeY(); --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (nonexistent) -@@ -1,67 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public class CellNode extends AbstractNode --{ -- 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (nonexistent) -@@ -1,348 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --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 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) -- { -- _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(); -- } -- -- public final List debugPath() -- { -- final List 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 void getNeighbors() -- { -- if (!_current.getLoc().canGoAll()) -- { -- 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); -- } -- -- // SouthEast -- if ((nodeE != null) && (nodeS != null)) -- { -- if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) -- { -- addNode(x + 1, y + 1, z, true); -- } -- } -- -- // SouthWest -- if ((nodeS != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) -- { -- addNode(x - 1, y + 1, z, true); -- } -- } -- -- // NorthEast -- if ((nodeN != null) && (nodeE != null)) -- { -- if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) -- { -- addNode(x + 1, y - 1, z, true); -- } -- } -- -- // NorthWest -- if ((nodeN != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) -- { -- addNode(x - 1, y - 1, z, true); -- } -- } -- } -- -- private 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(); -- // reinit node if needed -- if (result.getLoc() != null) -- { -- result.getLoc().set(x, y, z); -- } -- else -- { -- result.setLoc(new NodeLoc(x, y, z)); -- } -- } -- -- return result; -- } -- -- private 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 boolean isHighWeight(int x, int y, int z) -- { -- final CellNode result = getNode(x, y, z); -- if (result == null) -- { -- return true; -- } -- -- if (!result.getLoc().canGoAll()) -- { -- return true; -- } -- if (Math.abs(result.getLoc().getZ() - z) > 16) -- { -- return true; -- } -- -- return false; -- } -- -- private 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (working copy) -@@ -0,0 +1,87 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+ -+/** -+ * @author Hasha -+ */ -+public class Node -+{ -+ // node coords and nswe flag -+ private GeoLocation _loc; -+ -+ // node parent (for reverse path construction) -+ private Node _parent; -+ // node child (for moving over nodes during iteration) -+ private Node _child; -+ -+ // node G cost (movement cost = parent movement cost + current movement cost) -+ private double _cost = -1000; -+ -+ public void setLoc(int x, int y, int z) -+ { -+ _loc = new GeoLocation(x, y, z); -+ } -+ -+ public GeoLocation getLoc() -+ { -+ return _loc; -+ } -+ -+ public void setParent(Node parent) -+ { -+ _parent = parent; -+ } -+ -+ public Node getParent() -+ { -+ return _parent; -+ } -+ -+ public void setChild(Node child) -+ { -+ _child = child; -+ } -+ -+ public Node getChild() -+ { -+ return _child; -+ } -+ -+ public void setCost(double cost) -+ { -+ _cost = cost; -+ } -+ -+ public double getCost() -+ { -+ return _cost; -+ } -+ -+ public void free() -+ { -+ // reset node location -+ _loc = null; -+ -+ // reset node parent, child and cost -+ _parent = null; -+ _child = null; -+ _cost = -1000; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (working copy) -@@ -0,0 +1,306 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import java.util.concurrent.locks.ReentrantLock; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+ -+/** -+ * @author DS, Hasha; Credits to Diamond -+ */ -+public class NodeBuffer -+{ -+ private final ReentrantLock _lock = new ReentrantLock(); -+ private final int _size; -+ private final Node[][] _buffer; -+ -+ // center coordinates -+ private int _cx = 0; -+ private int _cy = 0; -+ -+ // target coordinates -+ private int _gtx = 0; -+ private int _gty = 0; -+ private short _gtz = 0; -+ -+ private Node _current = null; -+ -+ /** -+ * Constructor of NodeBuffer. -+ * @param size : one dimension size of buffer -+ */ -+ public NodeBuffer(int size) -+ { -+ // set size -+ _size = size; -+ -+ // initialize buffer -+ _buffer = new Node[size][size]; -+ for (int x = 0; x < size; x++) -+ { -+ for (int y = 0; y < size; y++) -+ { -+ _buffer[x][y] = 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 Node : first node of path -+ */ -+ public Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) -+ { -+ // set coordinates (middle of the line (gox,goy) - (gtx,gty), will be in the center of the buffer) -+ _cx = gox + ((gtx - gox - _size) / 2); -+ _cy = goy + ((gty - goy - _size) / 2); -+ -+ _gtx = gtx; -+ _gty = gty; -+ _gtz = gtz; -+ -+ _current = getNode(gox, goy, goz); -+ _current.setCost(getCostH(gox, goy, goz)); -+ -+ int count = 0; -+ do -+ { -+ // reached target? -+ if ((_current.getLoc().getGeoX() == _gtx) && (_current.getLoc().getGeoY() == _gty) && (Math.abs(_current.getLoc().getZ() - _gtz) < 8)) -+ { -+ return _current; -+ } -+ -+ // expand current node -+ expand(); -+ -+ // move pointer -+ _current = _current.getChild(); -+ } -+ while ((_current != null) && (count++ < Config.MAX_ITERATIONS)); -+ -+ return null; -+ } -+ -+ public boolean isLocked() -+ { -+ return _lock.tryLock(); -+ } -+ -+ public void free() -+ { -+ _current = null; -+ -+ for (Node[] nodes : _buffer) -+ { -+ for (Node node : nodes) -+ { -+ if (node.getLoc() != null) -+ { -+ node.free(); -+ } -+ } -+ } -+ -+ _lock.unlock(); -+ } -+ -+ /** -+ * Check _current Node and add its neighbors to the buffer. -+ */ -+ private final void expand() -+ { -+ // can't move anywhere, don't expand -+ final byte nswe = _current.getLoc().getNSWE(); -+ if (nswe == 0) -+ { -+ return; -+ } -+ -+ // get geo coords of the node to be expanded -+ final int x = _current.getLoc().getGeoX(); -+ final int y = _current.getLoc().getGeoY(); -+ final short z = (short) _current.getLoc().getZ(); -+ -+ // can move north, expand -+ if ((nswe & GeoStructure.CELL_FLAG_N) != 0) -+ { -+ addNode(x, y - 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move south, expand -+ if ((nswe & GeoStructure.CELL_FLAG_S) != 0) -+ { -+ addNode(x, y + 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_W) != 0) -+ { -+ addNode(x - 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_E) != 0) -+ { -+ addNode(x + 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move north-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NW) != 0) -+ { -+ addNode(x - 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move north-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NE) != 0) -+ { -+ addNode(x + 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SW) != 0) -+ { -+ addNode(x - 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SE) != 0) -+ { -+ addNode(x + 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ } -+ -+ /** -+ * Returns node, if it exists in buffer. -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param z : node Z coord -+ * @return Node : node, if exits in buffer -+ */ -+ private final Node getNode(int x, int y, short z) -+ { -+ // check node X out of coordinates -+ final int ix = x - _cx; -+ if ((ix < 0) || (ix >= _size)) -+ { -+ return null; -+ } -+ -+ // check node Y out of coordinates -+ final int iy = y - _cy; -+ if ((iy < 0) || (iy >= _size)) -+ { -+ return null; -+ } -+ -+ // get node -+ final Node result = _buffer[ix][iy]; -+ -+ // check and update -+ if (result.getLoc() == null) -+ { -+ result.setLoc(x, y, z); -+ } -+ -+ // return node -+ return result; -+ } -+ -+ /** -+ * Add node given by coordinates to the buffer. -+ * @param x : geo X coord -+ * @param y : geo Y coord -+ * @param z : geo Z coord -+ * @param weight : weight of movement to new node -+ */ -+ private final void addNode(int x, int y, short z, int weight) -+ { -+ // get node to be expanded -+ final Node node = getNode(x, y, z); -+ if (node == null) -+ { -+ return; -+ } -+ -+ // Z distance between nearby cells is higher than cell size -+ if (node.getLoc().getZ() > (z + (2 * GeoStructure.CELL_HEIGHT))) -+ { -+ return; -+ } -+ -+ // node was already expanded, return -+ if (node.getCost() >= 0) -+ { -+ return; -+ } -+ -+ node.setParent(_current); -+ if (node.getLoc().getNSWE() != (byte) 0xFF) -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + (weight * Config.OBSTACLE_MULTIPLIER)); -+ } -+ else -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + weight); -+ } -+ -+ Node current = _current; -+ int count = 0; -+ while ((current.getChild() != null) && (count < (Config.MAX_ITERATIONS * 4))) -+ { -+ count++; -+ if (current.getChild().getCost() > node.getCost()) -+ { -+ node.setChild(current.getChild()); -+ break; -+ } -+ current = current.getChild(); -+ } -+ -+ if (count >= (Config.MAX_ITERATIONS * 4)) -+ { -+ System.err.println("Pathfinding: too long loop detected, cost:" + node.getCost()); -+ } -+ -+ current.setChild(node); -+ } -+ -+ /** -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param i : node Z coord -+ * @return double : node cost -+ */ -+ private final double getCostH(int x, int y, int i) -+ { -+ final int dX = x - _gtx; -+ final int dY = y - _gty; -+ final int dZ = (i - _gtz) / GeoStructure.CELL_HEIGHT; -+ -+ // return (Math.abs(dX) + Math.abs(dY) + Math.abs(dZ)) * Config.HEURISTIC_WEIGHT; // Manhattan distance -+ return Math.sqrt((dX * dX) + (dY * dY) + (dZ * dZ)) * Config.HEURISTIC_WEIGHT; // Direct distance -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -- --/** -- * @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 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 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; -- // return super.hashCode(); -- } -- -- @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; -- if (_x != other._x) -- { -- return false; -- } -- if (_y != other._y) -- { -- return false; -- } -- if (_goNorth != other._goNorth) -- { -- return false; -- } -- if (_goEast != other._goEast) -- { -- return false; -- } -- if (_goSouth != other._goSouth) -- { -- return false; -- } -- if (_goWest != other._goWest) -- { -- return false; -- } -- if (_geoHeight != other._geoHeight) -- { -- return false; -- } -- return true; -- } --} -Index: java/org/l2jmobius/gameserver/model/actor/Creature.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/Creature.java (revision 8428) -+++ java/org/l2jmobius/gameserver/model/actor/Creature.java (working copy) -@@ -65,8 +65,6 @@ - import org.l2jmobius.gameserver.enums.TeleportWhereType; - import org.l2jmobius.gameserver.enums.UserInfoType; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; - import org.l2jmobius.gameserver.instancemanager.IdManager; - import org.l2jmobius.gameserver.instancemanager.MapRegionManager; - import org.l2jmobius.gameserver.instancemanager.QuestManager; -@@ -2566,7 +2564,7 @@ - - public boolean disregardingGeodata; - public int onGeodataPathIndex; -- public List geoPath; -+ public List geoPath; - public int geoPathAccurateTx; - public int geoPathAccurateTy; - public int geoPathGtx; -@@ -3443,7 +3441,7 @@ - if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) - { - // Path calculation -- overrides previous movement check -- m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); -+ m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); - if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found - { - if (isPlayer() && !_isFlying && !isInWater) -Index: java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (revision 8424) -+++ java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (working copy) -@@ -12746,7 +12746,7 @@ - { - if (Config.CORRECT_PLAYER_Z) - { -- final int nearestZ = GeoEngine.getInstance().getHigherHeight(getX(), getY(), getZ()); -+ final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); - if (getZ() < nearestZ) - { - teleToLocation(new Location(getX(), getY(), nearestZ)); -Index: java/org/l2jmobius/gameserver/util/GeoUtils.java -=================================================================== ---- java/org/l2jmobius/gameserver/util/GeoUtils.java (revision 8409) -+++ java/org/l2jmobius/gameserver/util/GeoUtils.java (working copy) -@@ -19,7 +19,7 @@ - import java.awt.Color; - - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; - import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; - -@@ -26,25 +26,25 @@ - /** - * @author HorridoJoho - */ --public final class GeoUtils -+public class GeoUtils - { - public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); - - final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); - - while (iter.next()) - { -- final int wx = GeoEngine.getInstance().getWorldX(iter.x()); -- final int wy = GeoEngine.getInstance().getWorldY(iter.y()); -+ final int wx = GeoEngine.getWorldX(iter.x()); -+ final int wy = GeoEngine.getWorldY(iter.y()); - - prim.addPoint(Color.RED, wx, wy, z); - } -@@ -53,21 +53,21 @@ - - public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); -- int wy = GeoEngine.getInstance().getWorldY(prevY); -+ int wx = GeoEngine.getWorldX(prevX); -+ int wy = GeoEngine.getWorldY(prevY); - int wz = iter.z(); - prim.addPoint(Color.RED, wx, wy, wz); - -@@ -78,8 +78,8 @@ - - if ((curX != prevX) || (curY != prevY)) - { -- wx = GeoEngine.getInstance().getWorldX(curX); -- wy = GeoEngine.getInstance().getWorldY(curY); -+ wx = GeoEngine.getWorldX(curX); -+ wy = GeoEngine.getWorldY(curY); - wz = iter.z(); - - prim.addPoint(Color.RED, wx, wy, wz); -@@ -93,7 +93,7 @@ - - private static Color getDirectionColor(int x, int y, int z, int nswe) - { -- if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) -+ if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) - { - return Color.GREEN; - } -@@ -109,9 +109,8 @@ - int iPacket = 0; - - ExServerPrimitive exsp = null; -- final GeoEngine ge = GeoEngine.getInstance(); -- final int playerGx = ge.getGeoX(player.getX()); -- final int playerGy = ge.getGeoY(player.getY()); -+ final int playerGx = GeoEngine.getGeoX(player.getX()); -+ final int playerGy = GeoEngine.getGeoY(player.getY()); - for (int dx = -geoRadius; dx <= geoRadius; ++dx) - { - for (int dy = -geoRadius; dy <= geoRadius; ++dy) -@@ -135,12 +134,12 @@ - final int gx = playerGx + dx; - final int gy = playerGy + dy; - -- final int x = ge.getWorldX(gx); -- final int y = ge.getWorldY(gy); -- final int z = ge.getNearestZ(gx, gy, player.getZ()); -+ final int x = GeoEngine.getWorldX(gx); -+ final int y = GeoEngine.getWorldY(gy); -+ final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); - - // north arrow -- Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); -+ Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); - 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); -@@ -147,7 +146,7 @@ - exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z); - - // east arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); - 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); -@@ -154,13 +153,13 @@ - exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z); - - // south arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); - 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, Cell.NSWE_WEST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); - 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); -@@ -188,15 +187,15 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_EAST; -+ return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_EAST; -+ return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; - } - else - { -- return Cell.NSWE_EAST; -+ return GeoStructure.CELL_FLAG_E; // Direction.EAST; - } - } - else if (x < lastX) // west -@@ -203,26 +202,27 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_WEST; -+ return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_WEST; -+ return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; - } - else - { -- return Cell.NSWE_WEST; -+ return GeoStructure.CELL_FLAG_W; // Direction.WEST; - } - } -- else // unchanged x -+ else -+ // unchanged x - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH; -+ return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH; -+ return GeoStructure.CELL_FLAG_N; // Direction.NORTH; - } - else - { -Index: java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java -=================================================================== ---- java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (nonexistent) -+++ java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (working copy) -@@ -0,0 +1,386 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.tools.geodataconverter; -+ -+import java.io.BufferedOutputStream; -+import java.io.File; -+import java.io.FileOutputStream; -+import java.io.RandomAccessFile; -+import java.nio.ByteOrder; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.Scanner; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.commons.util.PropertiesParser; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoDataConverter -+{ -+ private static GeoFormat _format; -+ private static ABlock[][] _blocks; -+ -+ public static void main(String[] args) -+ { -+ // initialize config -+ loadGeoengineConfigs(); -+ -+ // get geodata format -+ String type = ""; -+ try (Scanner scn = new Scanner(System.in)) -+ { -+ while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) -+ { -+ System.out.println("GeoDataConverter: Select source geodata type:"); -+ System.out.println(" J: L2J (e.g. 23_22.l2j)"); -+ System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); -+ System.out.println(" E: Exit"); -+ System.out.print("Choice: "); -+ type = scn.next(); -+ } -+ } -+ if (type.equalsIgnoreCase("E")) -+ { -+ System.exit(0); -+ } -+ -+ _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; -+ -+ // start conversion -+ System.out.println("GeoDataConverter: Converting all " + _format + " files."); -+ -+ // initialize geodata container -+ _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files -+ int converted = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) -+ { -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) -+ { -+ final String input = String.format(_format.getFilename(), rx, ry); -+ final String filepath = Config.GEODATA_PATH; -+ final File f = new File(filepath + input); -+ if (f.exists() && !f.isDirectory()) -+ { -+ // load geodata -+ if (!loadGeoBlocks(input)) -+ { -+ System.out.println("GeoDataConverter: Unable to load " + input + " region file."); -+ continue; -+ } -+ -+ // recalculate nswe -+ if (!recalculateNswe()) -+ { -+ System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); -+ continue; -+ } -+ -+ // save geodata -+ final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); -+ if (!saveGeoBlocks(output)) -+ { -+ System.out.println("GeoDataConverter: Unable to save " + output + " region file."); -+ continue; -+ } -+ -+ converted++; -+ System.out.println("GeoDataConverter: Created " + output + " region file."); -+ } -+ } -+ } -+ System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); -+ } -+ -+ /** -+ * Loads geo blocks from buffer of the region file. -+ * @param filename : The name of the to load. -+ * @return boolean : True when successful. -+ */ -+ private static boolean loadGeoBlocks(String filename) -+ { -+ // region file is load-able, try to load it -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) -+ { -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) -+ if (_format == GeoFormat.L2OFF) -+ { -+ for (int i = 0; i < 18; i++) -+ { -+ buffer.get(); -+ } -+ } -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ if (_format == GeoFormat.L2J) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2J: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2J: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ else -+ { -+ // get block type -+ final short type = buffer.getShort(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ default: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ if (buffer.remaining() > 0) -+ { -+ System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ return false; -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); -+ return false; -+ } -+ } -+ -+ /** -+ * Recalculate diagonal flags for the region file. -+ * @return boolean : True when successful. -+ */ -+ private static boolean recalculateNswe() -+ { -+ try -+ { -+ for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) -+ { -+ for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) -+ { -+ // get block -+ final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // skip flat blocks -+ if (block instanceof BlockFlat) -+ { -+ continue; -+ } -+ -+ // for complex and multilayer blocks go though all layers -+ short height = Short.MAX_VALUE; -+ int index; -+ while ((index = block.getIndexBelow(x, y, height)) != -1) -+ { -+ // get height and nswe -+ height = block.getHeight(index); -+ byte nswe = block.getNswe(index); -+ -+ // update nswe with diagonal flags -+ nswe = updateNsweBelow(x, y, height, nswe); -+ -+ // set nswe of the cell -+ block.setNswe(index, nswe); -+ } -+ } -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ /** -+ * Updates the NSWE flag with diagonal flags. -+ * @param x : Geodata X coordinate. -+ * @param y : Geodata Y coordinate. -+ * @param z : Geodata Z coordinate. -+ * @param nsweValue : NSWE flag to be updated. -+ * @return byte : Updated NSWE flag. -+ */ -+ private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) -+ { -+ byte nswe = nsweValue; -+ -+ // calculate virtual layer height -+ final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); -+ -+ // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) -+ final byte nsweN = getNsweBelow(x, y - 1, height); -+ final byte nsweS = getNsweBelow(x, y + 1, height); -+ final byte nsweW = getNsweBelow(x - 1, y, height); -+ final byte nsweE = getNsweBelow(x + 1, y, height); -+ -+ // north-west -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NW; -+ } -+ -+ // north-east -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NE; -+ } -+ -+ // south-west -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SW; -+ } -+ -+ // south-east -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SE; -+ } -+ -+ return nswe; -+ } -+ -+ private static byte getNsweBelow(int geoX, int geoY, short worldZ) -+ { -+ // out of geo coordinates -+ if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) -+ { -+ return 0; -+ } -+ -+ // out of geo coordinates -+ if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) -+ { -+ return 0; -+ } -+ -+ // get block -+ final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // get index, when valid, return nswe -+ final int index = block.getIndexBelow(geoX, geoY, worldZ); -+ return index == -1 ? 0 : block.getNswe(index); -+ } -+ -+ /** -+ * Save region file to file. -+ * @param filename : The name of file to save. -+ * @return boolean : True when successful. -+ */ -+ private static boolean saveGeoBlocks(String filename) -+ { -+ try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) -+ { -+ // loop over region blocks and save each block -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[ix][iy].saveBlock(bos); -+ } -+ } -+ -+ // flush data to file -+ bos.flush(); -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ private static void loadGeoengineConfigs() -+ { -+ final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); -+ Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); -+ Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); -+ Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); -+ Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); -+ Config.PATHFINDING = geoData.getBoolean("PathFinding", true); -+ Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -+ Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); -+ Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); -+ Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); -+ Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); -+ Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); -+ } -+} -\ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/geodata/Readme.txt b/L2J_Mobius_2.5_Underground/dist/game/data/geodata/Readme.txt index a480c8c380..2611882e56 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/geodata/Readme.txt +++ b/L2J_Mobius_2.5_Underground/dist/game/data/geodata/Readme.txt @@ -23,8 +23,8 @@ a - Prerequisites b - Make it work ---------------------------------------------- -To make geodata working: -* unpack your geodata files into "/data/geodata" folder -* open "/config/GeoEngine.ini" with your favorite text editor and then edit following config: - - CoordSynchronize = 2 -* If you do not use any geodata files, the server will automatically change this setting to -1. +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. diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java index 037b29d579..89425b0d85 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java +++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java @@ -295,7 +295,7 @@ public class FourSepulchers extends AbstractNpcAI implements IXmlReader { if ((npc != null) && !npc.isDead()) { - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), npc.getSpawn().getLocation().getX() + getRandom(-400, 400), npc.getSpawn().getLocation().getY() + getRandom(-400, 400), npc.getZ(), npc.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), npc.getSpawn().getLocation().getX() + getRandom(-400, 400), npc.getSpawn().getLocation().getY() + getRandom(-400, 400), npc.getZ(), npc.getInstanceWorld()); if (Util.calculateDistance(npc, npc.getSpawn().getLocation(), false, false) < 600) { npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java index 7aa8789df0..00bffa39b8 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java +++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java @@ -270,7 +270,7 @@ public class PrimevalIsle extends AbstractNpcAI final double cos = Math.cos(radian); final int newX = (int) (npc.getX() + (cos * distance)); final int newY = (int) (npc.getY() + (sin * distance)); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, npc.getZ(), npc.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, npc.getZ(), npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, loc, 0); } else if (ag_type == 1) diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java index 0a77f7404d..772f5fb5d7 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java +++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java @@ -78,7 +78,7 @@ public class BoyAndGirl extends AbstractNpcAI startQuestTimer("NPC_SHOUT", 10000 + (getRandom(5) * 1000), npc, null); npc.setRunning(); final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 200, 600); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); return super.onSpawn(npc); } @@ -86,7 +86,7 @@ public class BoyAndGirl extends AbstractNpcAI public void onMoveFinished(Npc npc) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 200, 600); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); super.onMoveFinished(npc); } diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java index 558f2631e6..3bcf3810cb 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java +++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java @@ -48,7 +48,7 @@ public class Devno extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java index 7525cc99e8..ce81f5c05c 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java +++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java @@ -48,7 +48,7 @@ public class Eleve extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java index 7d9150b156..f4e942c326 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java +++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java @@ -46,7 +46,7 @@ public class Handermonkey extends AbstractNpcAI { final int x = npc.getSpawn().getX() + (getRandom(-100, 100)); final int y = npc.getSpawn().getY() + (getRandom(-100, 100)); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x, y, npc.getZ(), npc.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x, y, npc.getZ(), npc.getInstanceWorld()); addMoveToDesire(npc, loc, 0); } else diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java index c993ce8936..7df61da041 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java +++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java @@ -48,7 +48,7 @@ public class Karonf extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java index 6e8cbee920..589d376f6b 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java +++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java @@ -48,7 +48,7 @@ public class Marsha extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java index c616abccc6..5c09d531c8 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java +++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java @@ -48,7 +48,7 @@ public class Morgan extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java index 3652b4ab02..fe20952121 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java +++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java @@ -48,7 +48,7 @@ public class Rubentis extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", 10000 + (getRandom(5) * 1000), npc, null); } diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java index beece64521..b0944f763d 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java +++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java @@ -63,7 +63,7 @@ public class Vortex extends AbstractNpcAI final int x = (int) (attackers.getX() + (600 * Math.cos(radians))); final int y = (int) (attackers.getY() + (600 * Math.sin(radians))); final int z = attackers.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(attackers.getX(), attackers.getY(), attackers.getZ(), x, y, z, attackers.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(attackers.getX(), attackers.getY(), attackers.getZ(), x, y, z, attackers.getInstanceWorld()); attackers.broadcastPacket(new FlyToLocation(attackers, x, y, z, FlyToLocation.FlyType.THROW_UP, 800, 800, 800)); attackers.setXYZ(loc); attackers.broadcastPacket(new ValidateLocation(attackers)); diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/others/FleeMonsters.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/others/FleeMonsters.java index 9346aebc95..b5de804b28 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/others/FleeMonsters.java +++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/others/FleeMonsters.java @@ -68,7 +68,7 @@ public class FleeMonsters extends AbstractNpcAI final int posX = (int) (npc.getX() + (FLEE_DISTANCE * Math.cos(radians))); final int posY = (int) (npc.getY() + (FLEE_DISTANCE * Math.sin(radians))); final int posZ = npc.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, attacker.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); return super.onAttack(npc, attacker, damage, isSummon); } diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index 524652cbc1..18511c6541 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { @@ -133,8 +133,8 @@ public class AdminGeodata implements IAdminCommandHandler } case "admin_geomap": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y + " (" + ((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + "," + ((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + " to " + ((((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + "," + ((((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + ")"); break; } diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java index f3dffa1227..76572f7f25 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java +++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java @@ -57,8 +57,8 @@ public class AdminMissingHtmls implements IAdminCommandHandler { case "admin_geomap_missing_htmls": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final int topLeftX = (x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE; final int topLeftY = (y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE; final int bottomRightX = (((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1; diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index fdb2319138..a0a34f1051 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -19,9 +19,9 @@ package handlers.admincommandhandlers; import java.util.List; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; +import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.util.BuilderUtil; @@ -44,13 +44,13 @@ public class AdminPathNode implements IAdminCommandHandler } if (activeChar.getTarget() != null) { - final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { BuilderUtil.sendSysMessage(activeChar, "No Route!"); return true; } - for (AbstractNodeLoc a : path) + for (Location a : path) { BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/Blink.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/Blink.java index 22f2c84eae..22697e8e20 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/Blink.java +++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/Blink.java @@ -88,7 +88,7 @@ public class Blink extends AbstractEffect final int y = effected.getY() + y1; final int z = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, destination, _flyType, _flySpeed, _flyDelay, _animationSpeed)); diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/Fear.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/Fear.java index 95a5afdf98..88c795e04b 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/Fear.java +++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/Fear.java @@ -100,7 +100,7 @@ public class Fear extends AbstractEffect final int posY = (int) (effected.getY() + (FEAR_RANGE * Math.sin(radians))); final int posZ = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); } } diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java index fddd94c60a..55c2e78d8a 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java +++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java @@ -57,7 +57,7 @@ public class FlyAway extends AbstractEffect final int y = (int) (effector.getY() - (nRadius * (dy / distance))); final int z = effector.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.broadcastPacket(new FlyToLocation(effected, destination, FlyType.THROW_UP)); effected.setXYZ(destination); diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java index 3c3f9aca62..14809ac499 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java +++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java @@ -179,7 +179,7 @@ public class KnockBack extends AbstractEffect final int x = (int) (effected.getX() + (_distance * Math.cos(radians))); final int y = (int) (effected.getY() + (_distance * Math.sin(radians))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, loc, _type, _speed, _delay, _animationSpeed)); diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/PullBack.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/PullBack.java index ed6a37eff6..38fe2fe9d0 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/PullBack.java +++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/PullBack.java @@ -73,7 +73,7 @@ public class PullBack extends AbstractEffect } // In retail, you get debuff, but you are not even moved if there is obstacle. You are still disabled from using skills and moving though. - if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effector.getInstanceWorld())) + if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effected.getInstanceWorld())) { effected.broadcastPacket(new FlyToLocation(effected, effector, _type, _speed, _delay, _animationSpeed)); effected.setXYZ(effector.getX(), effector.getY(), GeoEngine.getInstance().getHeight(effector.getX(), effector.getY(), effector.getZ()) + 10); diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java index d6f9664141..813e496c47 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java +++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java @@ -87,7 +87,7 @@ public class TeleportToSummon extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = summon.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java index d3c263978c..c9dc34ac22 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java +++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java @@ -76,7 +76,7 @@ public class TeleportToTarget extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java index f2148adb54..c7ca6f64cb 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java +++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java @@ -65,7 +65,7 @@ public class RollingDice implements IItemHandler final int x = player.getX() + x1; final int y = player.getY() + y1; final int z = player.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); Broadcast.toSelfAndKnownPlayers(player, new Dice(player.getObjectId(), itemId, number, destination.getX(), destination.getY(), destination.getZ())); final SystemMessage sm = new SystemMessage(SystemMessageId.C1_HAS_ROLLED_A_S2); diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/targethandlers/Ground.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/targethandlers/Ground.java index 328c9fb38a..2d4b94e385 100644 --- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/targethandlers/Ground.java +++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/handlers/targethandlers/Ground.java @@ -52,7 +52,7 @@ public class Ground implements ITargetTypeHandler return null; } - if (!GeoEngine.getInstance().canSeeTarget(creature, worldPosition)) + if (!GeoEngine.getInstance().canSeeLocation(creature, worldPosition)) { if (sendMessage) { diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/Config.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/Config.java index 1ecc5b7454..794dc56608 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/Config.java @@ -61,6 +61,7 @@ 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; @@ -942,12 +943,17 @@ public class Config // GeoEngine // -------------------------------------------------- public static Path GEODATA_PATH; + public static GeoType GEODATA_TYPE; public static boolean PATHFINDING; public static String PATHFIND_BUFFERS; - public static float LOW_WEIGHT; - public static float MEDIUM_WEIGHT; - public static float HIGH_WEIGHT; - public static float DIAGONAL_WEIGHT; + public static int MOVE_WEIGHT; + public static int MOVE_WEIGHT_DIAG; + public static int OBSTACLE_WEIGHT; + 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; /** Attribute System */ public static int S_WEAPON_STONE; @@ -2491,12 +2497,17 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); + GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); - MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); - HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); - DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); + MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); + OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); + HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); + MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); + MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); // Load AllowedPlayerRaces config file (if exists) final PropertiesParser AllowedPlayerRaces = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE); diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/commons/util/CommonUtil.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/commons/util/CommonUtil.java index 14ae3a4560..ab7837d827 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/commons/util/CommonUtil.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/commons/util/CommonUtil.java @@ -584,4 +584,15 @@ public class CommonUtil final DecimalFormat formatter = new DecimalFormat(format, new DecimalFormatSymbols(Locale.ENGLISH)); return formatter.format(value); } + + /** + * @param numToTest : The number to test. + * @param min : The minimum limit. + * @param max : The maximum limit. + * @return the number or one of the limit (mininum / maximum). + */ + public static int limit(int numToTest, int min, int max) + { + return (numToTest > max) ? max : ((numToTest < min) ? min : numToTest); + } } diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/commons/util/Point2D.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/commons/util/Point2D.java new file mode 100644 index 0000000000..ebb6272e5e --- /dev/null +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/commons/util/Point2D.java @@ -0,0 +1,180 @@ +/* + * 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 . + */ +package org.l2jmobius.commons.util; + +import java.util.Objects; + +/** + * A datatype used to retain a 2D (x/y) point. It got the capability to be set and cleaned. + */ +public class Point2D +{ + protected volatile int _x; + protected volatile int _y; + + public Point2D(int x, int y) + { + _x = x; + _y = y; + } + + @Override + public Point2D clone() + { + return new Point2D(_x, _y); + } + + @Override + public String toString() + { + return _x + ", " + _y; + } + + @Override + public int hashCode() + { + return Objects.hash(_x, _y); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final Point2D other = (Point2D) obj; + return (_x == other._x) && (_y == other._y); + } + + /** + * @param x : The X coord to test. + * @param y : The Y coord to test. + * @return True if all coordinates equals this {@link Point2D} coordinates. + */ + public boolean equals(int x, int y) + { + return (_x == x) && (_y == y); + } + + public int getX() + { + return _x; + } + + public void setX(int x) + { + _x = x; + } + + public int getY() + { + return _y; + } + + public void setY(int y) + { + _y = y; + } + + public void set(int x, int y) + { + _x = x; + _y = y; + } + + /** + * Refresh the current {@link Point2D} using a reference {@link Point2D} and a distance. The new destination is calculated to go in opposite side of the {@link Point2D} reference.
+ *
+ * This method is perfect to calculate fleeing characters position. + * @param referenceLoc : The Point2D used as position. + * @param distance : The distance to be set between current and new position. + */ + public void setFleeing(Point2D referenceLoc, int distance) + { + final double xDiff = referenceLoc.getX() - _x; + final double yDiff = referenceLoc.getY() - _y; + + final double yxRation = Math.abs(xDiff / yDiff); + + final int y = (int) (distance / (yxRation + 1)); + final int x = (int) (y * yxRation); + + _x += (xDiff < 0 ? x : -x); + _y += (yDiff < 0 ? y : -y); + } + + public void clean() + { + _x = 0; + _y = 0; + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @return The distance between this {@Point2D} and some given coordinates. + */ + public double distance2D(int x, int y) + { + final double dx = (double) _x - x; + final double dy = (double) _y - y; + + return Math.sqrt((dx * dx) + (dy * dy)); + } + + /** + * @param point : The {@link Point2D} to test. + * @return The distance between this {@Point2D} and the {@link Point2D} set as parameter. + */ + public double distance2D(Point2D point) + { + return distance2D(point.getX(), point.getY()); + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of some given coordinates. + */ + public boolean isIn2DRadius(int x, int y, int radius) + { + return distance2D(x, y) < radius; + } + + /** + * @param point : The Point2D to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of the {@link Point2D} set as parameter. + */ + public boolean isIn2DRadius(Point2D point, int radius) + { + return distance2D(point) < radius; + } +} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/AttackableAI.java index 547963e3e8..dc29d4cf6e 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/AttackableAI.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/ai/AttackableAI.java @@ -578,7 +578,7 @@ public class AttackableAI extends CreatureAI } // Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast) - final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); + final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); moveTo(moveLoc.getX(), moveLoc.getY(), moveLoc.getZ()); } } @@ -801,7 +801,7 @@ public class AttackableAI extends CreatureAI final int newZ = npc.getZ() + 30; // Mobius: Verify destination. Prevents wall collision issues and fixes monsters not avoiding obstacles. - moveTo(GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); + moveTo(GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); } return; } diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/data/SpawnTable.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/data/SpawnTable.java index a81201f69e..19f0db5b3e 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/data/SpawnTable.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/data/SpawnTable.java @@ -114,8 +114,8 @@ public class SpawnTable } // XML file for spawn - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); // Write info to XML @@ -192,8 +192,8 @@ public class SpawnTable if (update) { - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = spawn.getNpcSpawnTemplate() != null ? spawn.getNpcSpawnTemplate().getSpawnTemplate().getFile() : new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); final File tempFile = new File(spawnFile.getAbsolutePath().substring(Config.DATAPACK_ROOT.getAbsolutePath().length() + 1).replace('\\', '/') + ".tmp"); try diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/enums/GeoType.java similarity index 69% rename from L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java rename to L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/enums/GeoType.java index 7223b5b2be..f77aa5eac4 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/enums/GeoType.java @@ -14,20 +14,22 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.l2jmobius.gameserver.geoengine.pathfinding; +package org.l2jmobius.gameserver.enums; -/** - * @author -Nemesiss- - */ -public abstract class AbstractNodeLoc +public enum GeoType { - public abstract int getX(); + L2J("%d_%d.l2j"), + L2OFF("%d_%d_conv.dat"); - public abstract int getY(); + private final String _filename; - public abstract int getZ(); + private GeoType(String filename) + { + _filename = filename; + } - public abstract int getNodeX(); - - public abstract int getNodeY(); -} + public String getFilename() + { + return _filename; + } +} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java new file mode 100644 index 0000000000..e62f50a8df --- /dev/null +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java @@ -0,0 +1,144 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 9d255a5f54..7bef9c770b 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,72 +16,63 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.channels.FileChannel.MapMode; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.logging.Level; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; import java.util.logging.Logger; import org.l2jmobius.Config; +import org.l2jmobius.commons.util.CommonUtil; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; -import org.l2jmobius.gameserver.geoengine.geodata.IRegion; -import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; -import org.l2jmobius.gameserver.geoengine.geodata.Region; +import org.l2jmobius.gameserver.enums.GeoType; +import org.l2jmobius.gameserver.enums.MoveDirectionType; +import org.l2jmobius.gameserver.geoengine.geodata.ABlock; +import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; +import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; +import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; +import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.model.interfaces.ILocational; -import org.l2jmobius.gameserver.util.GeoUtils; -import org.l2jmobius.gameserver.util.LinePointIterator; -import org.l2jmobius.gameserver.util.LinePointIterator3D; -/** - * @author -Nemesiss-, HorridoJoho - */ public class GeoEngine { - private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - - private static final int WORLD_MIN_X = -655360; - private static final int WORLD_MIN_Y = -589824; - private static final int WORLD_MIN_Z = -16384; - - /** 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; - - /** The regions array */ - private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; - private static final int ELEVATED_SEE_OVER_DISTANCE = 2; - private static final int MAX_SEE_OVER_HEIGHT = 48; - protected GeoEngine() + private final ABlock[][] _blocks; + private final BlockNull _nullBlock; + + // Pre-allocated buffers. + private final BufferHolder[] _buffers; + + public GeoEngine() { LOGGER.info("GeoEngine: Initializing..."); - for (int i = 0; i < _regions.length(); i++) - { - _regions.set(i, NullRegion.INSTANCE); - } + // Initialize block container. + _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; + + // Load null block. + _nullBlock = new BlockNull(); + + // Initialize multilayer temporarily buffer. + BlockMultilayer.initialize(); + + // Load geo files according to geoengine config setup. int loaded = 0; try { @@ -92,23 +83,52 @@ public class GeoEngine final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); if (Files.exists(geoFilePath)) { - try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + // Region file is load-able, try to load it. + if (loadGeoBlocks(regionX, regionY)) { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); loaded++; } } + else + { + // Region file is not load-able, load null blocks. + loadNullBlocks(regionX, regionY); + } } } } catch (Exception e) { - LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + LOGGER.warning("GeoEngine: Failed to load geodata! " + e); System.exit(1); } - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + // Release multilayer block temporarily buffer. + BlockMultilayer.release(); + + String[] array = Config.PATHFIND_BUFFERS.split(";"); + _buffers = new BufferHolder[array.length]; + + int count = 0; + for (int i = 0; i < array.length; i++) + { + String buf = array[i]; + String[] args = buf.split("x"); + + try + { + int size = Integer.parseInt(args[1]); + count += size; + _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); + } + catch (Exception e) + { + LOGGER.warning("Could not load buffer setting:" + buf + ". " + e); + } + } + LOGGER.info("Loaded " + count + " node buffers."); + // Avoid wrong configs when no files are loaded. if ((loaded == 0) && Config.PATHFINDING) { @@ -118,616 +138,913 @@ public class GeoEngine } /** - * @param geoX - * @param geoY - * @return the region + * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer and log this situation. + * @param size : pre-calculated minimal required size + * @return NodeBuffer : buffer */ - private IRegion getRegion(int geoX, int geoY) + private NodeBuffer getBuffer(int size) { - final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); - if ((region < 0) || (region >= _regions.length())) + NodeBuffer current = null; + for (BufferHolder holder : _buffers) { - return null; - } - return _regions.get(region); - } - - /** - * @param filePath - * @param regionX - * @param regionY - * @throws IOException - */ - 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()).load().order(ByteOrder.LITTLE_ENDIAN))); - } - } - - /** - * @param regionX - * @param regionY - */ - public void unloadRegion(int regionX, int regionY) - { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); - } - - /** - * @param geoX - * @param geoY - * @return if geodata exist - */ - public boolean hasGeoPos(int geoX, int geoY) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return false; - } - return region.hasGeo(); - } - - /** - * Checks the specified position for available geodata. - * @param x the world x - * @param y the world y - * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise - */ - public boolean hasGeo(int x, int y) - { - return hasGeoPos(getGeoX(x), getGeoY(y)); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe check - */ - public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return true; - } - return region.checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe anti-corner cut - */ - public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) - { - boolean can = true; - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - return can && checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the nearest Z value - */ - public int getNearestZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNearestZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next lower Z value - */ - public int getNextLowerZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextLowerZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next higher Z value - */ - public int getNextHigherZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextHigherZ(geoX, geoY, worldZ); - } - - /** - * Gets the Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHeight(int x, int y, int z) - { - return getNearestZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next lower Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getLowerHeight(int x, int y, int z) - { - return getNextLowerZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next higher Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHigherHeight(int x, int y, int z) - { - return getNextHigherZ(getGeoX(x), getGeoY(y), z); - } - - /** - * @param worldX - * @return the geo X - */ - public int getGeoX(int worldX) - { - return (worldX - WORLD_MIN_X) / 16; - } - - /** - * @param worldY - * @return the geo Y - */ - public int getGeoY(int worldY) - { - return (worldY - WORLD_MIN_Y) / 16; - } - - /** - * @param worldZ - * @return the geo Z - */ - public int getGeoZ(int worldZ) - { - return (worldZ - WORLD_MIN_Z) / 16; - } - - /** - * @param geoX - * @return the world X - */ - public int getWorldX(int geoX) - { - return (geoX * 16) + WORLD_MIN_X + 8; - } - - /** - * @param geoY - * @return the world Y - */ - public int getWorldY(int geoY) - { - return (geoY * 16) + WORLD_MIN_Y + 8; - } - - /** - * @param geoZ - * @return the world Z - */ - public int getWorldZ(int geoZ) - { - return (geoZ * 16) + WORLD_MIN_Z + 8; - } - - /** - * Can see target. Doors as target always return true. Checks doors between. - * @param cha the character - * @param target the target - * @return {@code true} if the character can see the target (LOS), {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, WorldObject target) - { - if (target.isDoor()) - { - // Can always see doors. - return true; - } - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); - } - - /** - * Can see target. Checks doors between. - * @param cha the character - * @param worldPosition the world position - * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) - { - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param world - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @param tworld the target's instanceId - * @return - */ - public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) - { - return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instance - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) - { - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) - { - return false; - } - return canSeeTarget(x, y, z, tx, ty, tz); - } - - /** - * @param prevX - * @param prevY - * @param prevGeoZ - * @param curX - * @param curY - * @param nswe - * @return the LOS Z value - */ - private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) - { - if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) - { - throw new RuntimeException("Multiple directions!"); - } - - if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) - { - return getNearestZ(curX, curY, prevGeoZ); - } - - return getNextHigherZ(curX, curY, prevGeoZ); - } - - /** - * Can see target. Does not check doors between. - * @param xValue the x coordinate - * @param yValue the y coordinate - * @param zValue the z coordinate - * @param txValue the target's x coordinate - * @param tyValue the target's y coordinate - * @param tzValue the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) - { - int x = xValue; - int y = yValue; - int tx = txValue; - int ty = tyValue; - - int geoX = getGeoX(x); - int geoY = getGeoY(y); - int tGeoX = getGeoX(tx); - int tGeoY = getGeoY(ty); - - int z = getNearestZ(geoX, geoY, zValue); - int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - // Fastpath. - if ((geoX == tGeoX) && (geoY == tGeoY)) - { - if (hasGeoPos(tGeoX, tGeoY)) - { - return z == tz; - } - return true; - } - - if (tz > z) - { - int tmp = tx; - tx = x; - x = tmp; - - tmp = ty; - ty = y; - y = tmp; - - tmp = tz; - tz = z; - z = tmp; - - tmp = tGeoX; - tGeoX = geoX; - geoX = tmp; - - tmp = tGeoY; - tGeoY = geoY; - geoY = tmp; - } - - final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); - // First point is guaranteed to be available, skip it, we can always see our own position. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - final int prevZ = pointIter.z(); - int prevGeoZ = prevZ; - int ptIndex = 0; - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - - if ((curX == prevX) && (curY == prevY)) + // Find proper size of buffer. + if (holder._size < size) { continue; } - final int beeCurZ = pointIter.z(); - int curGeoZ = prevGeoZ; - - // Check if the position has geodata. - if (hasGeoPos(curX, curY)) + // Find unlocked NodeBuffer. + for (NodeBuffer buffer : holder._buffer) { - final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); - curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); - int maxHeight; - if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) + if (!buffer.isLocked()) { - maxHeight = z + MAX_SEE_OVER_HEIGHT; - } - else - { - maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; + continue; } - boolean canSeeThrough = false; - if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) + return buffer; + } + + LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); + + // NodeBuffer not found, allocate temporary buffer. + current = new NodeBuffer(holder._size); + current.isLocked(); + } + + return current; + } + + /** + * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + * @return boolean : True, when geodata file was loaded without problem. + */ + private boolean loadGeoBlocks(int regionX, int regionY) + { + final String filename = String.format(Config.GEODATA_TYPE.getFilename(), regionX, regionY); + final String filepath = Config.GEODATA_PATH + "\\" + filename; + + // Standard load. + try (RandomAccessFile raf = new RandomAccessFile(filepath, "r"); + FileChannel fc = raf.getChannel()) + { + // Initialize file buffer. + MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // Load 18B header for L2OFF geodata (1st and 2nd byte...region X and Y). + if (Config.GEODATA_TYPE == GeoType.L2OFF) + { + for (int i = 0; i < 18; i++) { - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) + buffer.get(); + } + } + + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Loop over region blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + if (Config.GEODATA_TYPE == GeoType.L2J) { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) - { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); + // Get block type. + final byte type = buffer.get(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + case GeoStructure.TYPE_MULTILAYER_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + default: + { + throw new IllegalArgumentException("Unknown block type: " + type); + } + } } else { - canSeeThrough = true; + // Get block type. + final short type = buffer.getShort(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + default: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + } } } + } + + // Check data consistency. + if (buffer.remaining() > 0) + { + LOGGER.warning("Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); + } + + // Loading was successful. + return true; + } + catch (Exception e) + { + // An error occured while loading, load null blocks. + LOGGER.warning("Error loading " + filename + " region file. " + e); + + // Replace whole region file with null blocks. + loadNullBlocks(regionX, regionY); + + // Loading was not successful. + return false; + } + } + + /** + * Loads null blocks. Used when no region file is detected or an error occurs during loading. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + */ + private void loadNullBlocks(int regionX, int regionY) + { + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Load all null blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + _blocks[blockX + ix][blockY + iy] = _nullBlock; + } + } + } + + /** + * Converts world X to geodata X. + * @param worldX + * @return int : Geo X + */ + public static int getGeoX(int worldX) + { + return (worldX - World.WORLD_X_MIN) >> 4; + } + + /** + * Converts world Y to geodata Y. + * @param worldY + * @return int : Geo Y + */ + public static int getGeoY(int worldY) + { + return (worldY - World.WORLD_Y_MIN) >> 4; + } + + /** + * Converts geodata X to world X. + * @param geoX + * @return int : World X + */ + public static int getWorldX(int geoX) + { + return (geoX << 4) + World.WORLD_X_MIN + 8; + } + + /** + * Converts geodata Y to world Y. + * @param geoY + * @return int : World Y + */ + public static int getWorldY(int geoY) + { + return (geoY << 4) + World.WORLD_Y_MIN + 8; + } + + /** + * Returns block of geodata on given coordinates. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return {@link ABlock} : Block of geodata. + */ + public ABlock getBlock(int geoX, int geoY) + { + final int x = geoX / GeoStructure.BLOCK_CELLS_X; + if ((x < 0) || (x >= GeoStructure.GEO_BLOCKS_X)) + { + return null; + } + final int y = geoY / GeoStructure.BLOCK_CELLS_Y; + if ((y < 0) || (y >= GeoStructure.GEO_BLOCKS_Y)) + { + return null; + } + return _blocks[x][y]; + } + + /** + * Check if geo coordinates has geo. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return boolean : True, if given geo coordinates have geodata + */ + public boolean hasGeoPos(int geoX, int geoY) + { + final ABlock block = getBlock(geoX, geoY); + return (block != null) && block.hasGeoPos(); + } + + /** + * Returns the height of cell, which is closest to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell geodata Z coordinate, closest to given coordinates. + */ + public short getHeightNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return (short) worldZ; + } + return block.getHeightNearest(geoX, geoY, worldZ); + } + + /** + * Returns the NSWE flag byte of cell, which is closes to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. + */ + public byte getNsweNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return GeoStructure.CELL_FLAG_ALL; + } + return block.getNsweNearest(geoX, geoY, worldZ); + } + + /** + * Check if world coordinates has geo. + * @param worldX : World X + * @param worldY : World Y + * @return boolean : True, if given world coordinates have geodata + */ + public boolean hasGeo(int worldX, int worldY) + { + return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param loc : The location used as reference. + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(Location loc) + { + return getHeightNearest(getGeoX(loc.getX()), getGeoY(loc.getY()), loc.getZ()); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param worldX : world x + * @param worldY : world y + * @param worldZ : world z + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(int worldX, int worldY, int worldZ) + { + return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); + } + + /** + * Check line of sight from {@link WorldObject} to {@link WorldObject}.
+ * @param object : The origin object. + * @param target : The target object. + * @return True, when object can see target. + */ + public boolean canSeeTarget(WorldObject object, WorldObject target) + { + // Can always see doors. + if (target.isDoor()) + { + return true; + } + + if (object.getInstanceWorld() != target.getInstanceWorld()) + { + return false; + } + + if (DoorData.getInstance().checkIfDoorsBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld())) + { + return false; + } + + // Get object's and target's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + double theight = 0; + if (target instanceof Creature) + { + theight += (((Creature) target).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + return canSee(object.getX(), object.getY(), object.getZ(), oheight, target.getX(), target.getY(), target.getZ(), theight) && canSee(target.getX(), target.getY(), target.getZ(), theight, object.getX(), object.getY(), object.getZ(), oheight); + } + + /** + * Check line of sight from {@link WorldObject} to {@link Location}.
+ * Note: The check uses {@link Location}'s real Z coordinate (e.g. point above ground), not its geodata representation. + * @param object : The origin object. + * @param position : The target position. + * @return True, when object can see position. + */ + public boolean canSeeLocation(WorldObject object, Location position) + { + // Get object and location coordinates. + int ox = object.getX(); + int oy = object.getY(); + int oz = object.getZ(); + int tx = position.getX(); + int ty = position.getY(); + int tz = position.getZ(); + + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld())) + { + return false; + } + + // Get object's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + // Perform geodata check. + return canSee(ox, oy, oz, oheight, tx, ty, tz, 0) && canSee(tx, ty, tz, 0, ox, oy, oz, oheight); + } + + /** + * Simple check for origin to target visibility.
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param oheight : The height of origin, used as start point. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param theight : The height of target, used as end point. + * @return True, when origin can see target. + */ + public boolean canSee(int ox, int oy, int oz, double oheight, int tx, int ty, int tz, double theight) + { + // Get origin geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get target geodata coordinates. + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check being on same cell and layer (index). + // Note: Get index must use origin height increased by cell height, the method returns index to height exclusive self. + int index = block.getIndexBelow(gox, goy, oz + GeoStructure.CELL_HEIGHT); + if (index < 0) + { + return false; + } + + if ((gox == gtx) && (goy == gty)) + { + return index == block.getIndexBelow(gtx, gty, tz + GeoStructure.CELL_HEIGHT); + } + + // Get ground and nswe flag. + int groundZ = block.getHeight(index); + int nswe = block.getNswe(index); + + // Get delta coordinates, slope of line (XY, XZ) and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double dz = (tz + theight) - (oz + oheight); + final double m = (double) dy / dx; + final double mz = dz / Math.sqrt((dx * dx) + (dy * dy)); + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); + + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) + { + // Set next cell in X direction. + gridX += mdt.getStepX(); + gox += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); - if (!canSeeThrough) - { - return false; - } + // Set next cell in Y direction. + gridY += mdt.getStepY(); + goy += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevGeoZ = curGeoZ; - ++ptIndex; + // Get block of the next cell. + block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get line of sight height (including Z slope). + double losz = oz + oheight + Config.MAX_OBSTACLE_HEIGHT; + losz += mz * Math.sqrt(((checkX - ox) * (checkX - ox)) + ((checkY - oy) * (checkY - oy))); + + // Check line of sight going though wall (vertical check). + + // Get index of particular layer, based on last iterated cell conditions. + boolean canMove = (nswe & dir) != 0; + if (canMove) + { + // No wall present, get next cell below current cell. + index = block.getIndexBelow(gox, goy, groundZ + GeoStructure.CELL_IGNORE_HEIGHT); + } + else + { + // Wall present, get next cell above current cell. + index = block.getIndexAbove(gox, goy, groundZ - (2 * GeoStructure.CELL_HEIGHT)); + } + + // Next cell's does not exist (no geodata with valid condition), return fail. + if (index < 0) + { + return false; + } + + // Get next cell's layer height. + int z = block.getHeight(index); + + // Perform sine of sight check (next cell is above line of sight line), return fail. + if (z > losz) + { + return false; + } + + // Next cell is accessible, update z and NSWE. + groundZ = z; + nswe = block.getNswe(index); } + // Iteration is completed, no obstacle is found. return true; } /** - * Move check. - * @param x the x coordinate - * @param y the y coordinate - * @param zValue the z coordinate - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tzValue the target's z coordinate - * @param instance the instance - * @return the last Location (x,y,z) where player can walk - just before wall + * Check movement from coordinates to coordinates.
+ * Note: The Z coordinates are supposed to be already validated geodata coordinates. + * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return True, when target coordinates are reachable from origin coordinates. */ - public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(x); - final int geoY = getGeoY(y); - final int z = getNearestZ(geoX, geoY, zValue); - final int tGeoX = getGeoX(tx); - final int tGeoY = getGeoY(ty); - final int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return new Location(x, y, getHeight(x, y, z)); - } - if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) - { - return new Location(x, y, getHeight(x, y, z)); + return false; } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = z; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return false; + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + int goz = getHeightNearest(gox, goy, oz); + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check movement within same cell. + if ((gox == gtx) && (goy == gty)) + { + return goz == getHeight(tx, ty, tz); + } + + // Get nswe flag. + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - // Can't move, return previous location. - return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check point heading into obstacle, if so return current point. + if ((nswe & dir) == 0) + { + return false; + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return false; + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != tz)) - { - // Different floors, return start location. - return new Location(x, y, z); - } - - return new Location(tx, ty, tz); + // When origin Z is target Z, the move is successful. + return goz == getHeight(tx, ty, tz); } /** - * Checks if its possible to move from one location to another. - * @param fromX the X coordinate to start checking from - * @param fromY the Y coordinate to start checking from - * @param fromZvalue the Z coordinate to start checking from - * @param toX the X coordinate to end checking at - * @param toY the Y coordinate to end checking at - * @param toZvalue the Z coordinate to end checking at - * @param instance the instance - * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + * Check movement from origin to target coordinates. Returns last available point in the checked path.
+ * Target X and Y reachable and Z is on same floor: + *
    + *
  • Location of the target with corrected Z value from geodata.
  • + *
+ * Target X and Y reachable but Z is on another floor: + *
    + *
  • Location of the origin with corrected Z value from geodata.
  • + *
+ * Target X and Y not reachable: + *
    + *
  • Last accessible location in destination to target.
  • + *
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return The {@link Location} representing last point of movement (e.g. just before wall). */ - public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + public Location getValidLocation(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(fromX); - final int geoY = getGeoY(fromY); - final int fromZ = getNearestZ(geoX, geoY, fromZvalue); - final int tGeoX = getGeoX(toX); - final int tGeoY = getGeoY(toY); - final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); - - if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) - { - return false; + return new Location(ox, oy, oz); } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = fromZ; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return new Location(ox, oy, oz); + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + final int gtz = getHeightNearest(gtx, gty, tz); + int goz = getHeightNearest(gox, goy, oz); + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - return false; - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check target cell is out of geodata grid (world coordinates). + if ((nx < 0) || (nx >= GeoStructure.GEO_CELLS_X) || (ny < 0) || (ny >= GeoStructure.GEO_CELLS_Y)) + { + return new Location(checkX, checkY, goz); + } + + // Check point heading into obstacle, if so return current (border) point. + if ((nswe & dir) == 0) + { + return new Location(checkX, checkY, goz); + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current (border) point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return new Location(checkX, checkY, goz); + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) - { - // Different floors. - return false; - } - - return true; + // Compare Z coordinates: + // If same, path is okay, return target point and fix its Z geodata coordinate. + // If not same, path is does not exist, return origin point. + return goz == gtz ? new Location(tx, ty, gtz) : new Location(ox, oy, oz); } + /** + * Returns the list of location objects as a result of complete path calculation. + * @param ox : origin x + * @param oy : origin y + * @param oz : origin z + * @param tx : target x + * @param ty : target y + * @param tz : target z + * @param instance + * @return {@code List} : complete path from nodes + */ + public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + { + // Get origin and check existing geo coords. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + if (!hasGeoPos(gox, goy)) + { + return Collections.emptyList(); + } + + int goz = getHeightNearest(gox, goy, oz); + + // Get target and check existing geo coords. + int gtx = getGeoX(tx); + int gty = getGeoY(ty); + if (!hasGeoPos(gtx, gty)) + { + return Collections.emptyList(); + } + + int gtz = getHeightNearest(gtx, gty, tz); + + // Prepare buffer for pathfinding calculations. + NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + if (buffer == null) + { + return Collections.emptyList(); + } + + // Find path. + List path = null; + try + { + path = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + if (path.isEmpty()) + { + return Collections.emptyList(); + } + } + catch (Exception e) + { + return Collections.emptyList(); + } + finally + { + buffer.free(); + } + + // Check path. + if (path.size() < 3) + { + return path; + } + + // Get path list iterator. + ListIterator point = path.listIterator(); + + // Get node A (origin). + int nodeAx = ox; + int nodeAy = oy; + int nodeAz = goz; + + // Get node B. + Location nodeB = point.next(); + + // Iterate thought the path to optimize it. + while (point.hasNext()) + { + // Get node C. + Location nodeC = path.get(point.nextIndex()); + + // Check movement from node A to node C. + if (canMoveToTarget(nodeAx, nodeAy, nodeAz, nodeC.getX(), nodeC.getY(), nodeC.getZ(), instance)) + { + // Can move from node A to node C. + // Remove node B. + point.remove(); + } + else + { + // Can not move from node A to node C. + // Set node A (node B is part of path, update A coordinates). + nodeAx = nodeB.getX(); + nodeAy = nodeB.getY(); + nodeAz = nodeB.getZ(); + } + + // Set node B. + nodeB = point.next(); + } + + return path; + } + + /** + * NodeBuffer container with specified size and count of separate buffers. + */ + private static class BufferHolder + { + final int _size; + final List _buffer; + + public BufferHolder(int size, int count) + { + _size = size; + _buffer = new ArrayList<>(count); + + for (int i = 0; i < count; i++) + { + _buffer.add(new NodeBuffer(size)); + } + } + } + + /** + * Returns the instance of the {@link GeoEngine}. + * @return {@link GeoEngine} : The instance. + */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -737,4 +1054,4 @@ public class GeoEngine { protected static final GeoEngine INSTANCE = new GeoEngine(); } -} +} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java deleted file mode 100644 index cc76b7523a..0000000000 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ /dev/null @@ -1,301 +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 . - */ -package org.l2jmobius.gameserver.geoengine; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; -import org.l2jmobius.gameserver.model.World; -import org.l2jmobius.gameserver.model.instancezone.Instance; - -/** - * @author -Nemesiss- - */ -public class GeoEnginePathfinding -{ - private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); - - private BufferInfo[] _buffers; - - protected GeoEnginePathfinding() - { - try - { - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - - _buffers = 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); - } - - _buffers[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"); - } - } - - public boolean pathNodesExist(short regionoffset) - { - return false; - } - - public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) - { - 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)))); - if (buffer == null) - { - return null; - } - - List path = null; - try - { - final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); - - if (result == null) - { - return null; - } - - path = constructPath(result); - } - catch (Exception e) - { - LOGGER.warning(e.getMessage()); - return null; - } - finally - { - buffer.free(); - } - - // check path - if (path.size() < 3) - { - return path; - } - - int currentX, currentY, currentZ; - ListIterator middlePoint; - - middlePoint = path.listIterator(); - currentX = x; - currentY = y; - currentZ = z; - - while (middlePoint.hasNext()) - { - final AbstractNodeLoc locMiddle = middlePoint.next(); - if (!middlePoint.hasNext()) - { - break; - } - - final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); - if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) - { - middlePoint.remove(); - } - else - { - currentX = locMiddle.getX(); - currentY = locMiddle.getY(); - currentZ = locMiddle.getZ(); - } - } - - return path; - } - - /** - * Convert geodata position to pathnode position - * @param geo_pos - * @return pathnode position - */ - public short getNodePos(int geo_pos) - { - return (short) (geo_pos >> 3); // OK? - } - - /** - * Convert node position to pathnode block position - * @param node_pos - * @return pathnode block position (0...255) - */ - public short getNodeBlock(int node_pos) - { - return (short) (node_pos % 256); - } - - public byte getRegionX(int node_pos) - { - return (byte) ((node_pos >> 8) + World.TILE_X_MIN); - } - - public byte getRegionY(int node_pos) - { - return (byte) ((node_pos >> 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 node_x rx - * @return - */ - public int calculateWorldX(short node_x) - { - return World.MAP_MIN_X + (node_x * 128) + 48; - } - - /** - * Convert pathnode y to World y position - * @param node_y - * @return - */ - public int calculateWorldY(short node_y) - { - return World.MAP_MIN_Y + (node_y * 128) + 48; - } - - private List constructPath(AbstractNode nodeValue) - { - final LinkedList path = new LinkedList<>(); - int previousDirectionX = Integer.MIN_VALUE; - int previousDirectionY = Integer.MIN_VALUE; - int directionX, directionY; - - AbstractNode node = nodeValue; - while (node.getParent() != null) - { - directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); - directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); - - // only add a new route point if moving direction changes - if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) - { - previousDirectionX = directionX; - previousDirectionY = directionY; - - path.addFirst(node.getLoc()); - node.setLoc(null); - } - - node = node.getParent(); - } - - return path; - } - - private CellNodeBuffer alloc(int size) - { - CellNodeBuffer current = null; - for (BufferInfo i : _buffers) - { - if (i.mapSize >= size) - { - for (CellNodeBuffer buf : i.buffer) - { - if (buf.lock()) - { - current = buf; - break; - } - } - if (current != null) - { - break; - } - - // not found, allocate temporary buffer - current = new CellNodeBuffer(i.mapSize); - current.lock(); - if (i.buffer.size() < i.count) - { - i.buffer.add(current); - break; - } - } - } - - return current; - } - - private static final class BufferInfo - { - final int mapSize; - final int count; - ArrayList buffer; - - public BufferInfo(int size, int cnt) - { - mapSize = size; - count = cnt; - buffer = new ArrayList<>(count); - } - } - - public static GeoEnginePathfinding getInstance() - { - return SingletonHolder.INSTANCE; - } - - private static class SingletonHolder - { - protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); - } -} diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java new file mode 100644 index 0000000000..49ec35aa1c --- /dev/null +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java @@ -0,0 +1,85 @@ +/* + * 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 . + */ +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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @param index : Index of the cell. + * @return short : Cell geodata Z coordinate, below given coordinates. + */ + public abstract byte getNswe(int index); +} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java new file mode 100644 index 0000000000..f5997bf287 --- /dev/null +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java @@ -0,0 +1,130 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java new file mode 100644 index 0000000000..9af9d2a28a --- /dev/null +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java @@ -0,0 +1,95 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java new file mode 100644 index 0000000000..31fbf5d477 --- /dev/null +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java similarity index 55% rename from L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java rename to L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java index 72a5a635c7..f77d21d84b 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java @@ -16,40 +16,53 @@ */ package org.l2jmobius.gameserver.geoengine.geodata; -/** - * @author HorridoJoho - */ -public final class NullRegion implements IRegion +public class BlockNull extends ABlock { - public static final NullRegion INSTANCE = new NullRegion(); - @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 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() + public boolean hasGeoPos() { 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java deleted file mode 100644 index 61ea4fcf82..0000000000 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java +++ /dev/null @@ -1,48 +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 . - */ -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() - { - } -} diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java deleted file mode 100644 index 3c8de1a7f7..0000000000 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java +++ /dev/null @@ -1,77 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java deleted file mode 100644 index 1ccdefe3da..0000000000 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java +++ /dev/null @@ -1,56 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java new file mode 100644 index 0000000000..691b164e0c --- /dev/null +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ +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_ALL = 0x0F; + + // 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; +} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java deleted file mode 100644 index 25eafc5a04..0000000000 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java +++ /dev/null @@ -1,42 +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 . - */ -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); -} diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java deleted file mode 100644 index 0d2c765002..0000000000 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java +++ /dev/null @@ -1,47 +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 . - */ -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(); -} diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java deleted file mode 100644 index 52df457360..0000000000 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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); - - // 1 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; - } -} diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java deleted file mode 100644 index fc924cb875..0000000000 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java +++ /dev/null @@ -1,92 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java deleted file mode 100644 index 5087513ed9..0000000000 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java +++ /dev/null @@ -1,87 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public abstract class AbstractNode -{ - private T _loc; - private AbstractNode _parent; - - public AbstractNode(T loc) - { - _loc = loc; - } - - public void setParent(AbstractNode p) - { - _parent = p; - } - - public AbstractNode getParent() - { - return _parent; - } - - public T getLoc() - { - return _loc; - } - - public void setLoc(T l) - { - _loc = l; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); - return result; - } - - @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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java deleted file mode 100644 index 3425234e0e..0000000000 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public class CellNode extends AbstractNode -{ - 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java deleted file mode 100644 index 522610bb7c..0000000000 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java +++ /dev/null @@ -1,348 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -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 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) - { - _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(); - } - - public final List debugPath() - { - final List 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 void getNeighbors() - { - if (!_current.getLoc().canGoAll()) - { - 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); - } - - // SouthEast - if ((nodeE != null) && (nodeS != null)) - { - if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) - { - addNode(x + 1, y + 1, z, true); - } - } - - // SouthWest - if ((nodeS != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) - { - addNode(x - 1, y + 1, z, true); - } - } - - // NorthEast - if ((nodeN != null) && (nodeE != null)) - { - if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) - { - addNode(x + 1, y - 1, z, true); - } - } - - // NorthWest - if ((nodeN != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) - { - addNode(x - 1, y - 1, z, true); - } - } - } - - private 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(); - // reinit node if needed - if (result.getLoc() != null) - { - result.getLoc().set(x, y, z); - } - else - { - result.setLoc(new NodeLoc(x, y, z)); - } - } - - return result; - } - - private 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 boolean isHighWeight(int x, int y, int z) - { - final CellNode result = getNode(x, y, z); - if (result == null) - { - return true; - } - - if (!result.getLoc().canGoAll()) - { - return true; - } - if (Math.abs(result.getLoc().getZ() - z) > 16) - { - return true; - } - - return false; - } - - private 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java new file mode 100644 index 0000000000..fa5ca43c2f --- /dev/null +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -0,0 +1,111 @@ +/* + * 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 . + */ +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 geodata values. + private int _geoX; + private int _geoY; + private byte _nswe; + + // 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; + + _costG = 0; + _costH = 0; + _costF = 0; + + _parent = null; + } + + public void setGeo(int gx, int gy, int gz, byte nswe) + { + super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); + + _geoX = gx; + _geoY = gy; + _nswe = nswe; + } + + 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 int getCostF() + { + return _costF; + } + + public Node getParent() + { + return _parent; + } + + @Override + public int compareTo(Node o) + { + return _costF - o._costF; + } +} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java new file mode 100644 index 0000000000..b464212c96 --- /dev/null +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -0,0 +1,368 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.awt.Color; +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; +import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; + +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 _opened; + // Container holding Nodes already explored. + private final List _closed; + + // Target coordinates. + private int _gtx; + private int _gty; + private int _gtz; + + // Pathfinding statistics. + private long _timeStamp; + private long _lastElapsedTime; + + 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 findPath(int gox, int goy, int goz, int gtx, int gty, int gtz) + { + // Set start timestamp. + _timeStamp = System.currentTimeMillis(); + + // 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)); + _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 constructPath() + { + // Create result. + final LinkedList 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; + } + + /** + * Creates list of Nodes to show debug path. + * @param debug : The debug packet to add debug informations in. + */ + public void debugPath(ExServerPrimitive debug) + { + // Add all opened node as yellow points. + for (Node n : _opened) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); + } + + // Add all opened node as blue points. + for (Node n : _closed) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); + } + } + + 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; + + _lastElapsedTime = System.currentTimeMillis() - _timeStamp; + _lock.unlock(); + } + + public long getElapsedTime() + { + return _lastElapsedTime; + } + + /** + * Expand the current {@link Node} by exploring its neighbors (axially and diagonally). + */ + private void expand() + { + // Movement is blocked, skip. + final byte nswe = _current.getNSWE(); + if (nswe == 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; + + // Can move north, expand. + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + } + + // Can move south, expand. + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + } + + // Can move west, expand. + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move east, expand. + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move north-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move north-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + } + + /** + * 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 weight : The weight of movement to the new node. + * @return The nswe of the added node. Blank, if not added. + */ + private byte addNode(int gx, int gy, int gzValue, int weight) + { + // 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]; + + // Set node geodata coordinates. + node.setGeo(gx, gy, gz, nswe); + + // 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, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + _opened.add(node); + _bufferIndex++; + return nswe; + } + + /** + * Calculate cost H value, calculated using diagonal distance method.
+ * 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. + final int dx = Math.abs(gx - _gtx); + final int dy = Math.abs(gy - _gty); + final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + + // Get diagonal and axial differences to the target. + final int dd = Math.min(dx, dy); + final int da = Math.max(dx, dy) - dd; + + // Calculate the diagonal distance of the node to the target. + return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + } +} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java deleted file mode 100644 index e3bad04b25..0000000000 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; - -/** - * @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 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 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; - // return super.hashCode(); - } - - @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; - if (_x != other._x) - { - return false; - } - if (_y != other._y) - { - return false; - } - if (_goNorth != other._goNorth) - { - return false; - } - if (_goEast != other._goEast) - { - return false; - } - if (_goSouth != other._goSouth) - { - return false; - } - if (_goWest != other._goWest) - { - return false; - } - if (_geoHeight != other._geoHeight) - { - return false; - } - return true; - } -} diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java index 1faf1ae423..ed261765e1 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java @@ -93,15 +93,15 @@ public class ZoneManager implements IXmlReader private static final Map SETTINGS = new HashMap<>(); private static final int SHIFT_BY = 15; - private static final int OFFSET_X = Math.abs(World.MAP_MIN_X >> SHIFT_BY); - private static final int OFFSET_Y = Math.abs(World.MAP_MIN_Y >> SHIFT_BY); + private static final int OFFSET_X = Math.abs(World.WORLD_X_MIN >> SHIFT_BY); + private static final int OFFSET_Y = Math.abs(World.WORLD_Y_MIN >> SHIFT_BY); private final Map, ConcurrentHashMap> _classZones = new ConcurrentHashMap<>(); private final Map _spawnTerritories = new ConcurrentHashMap<>(); private final AtomicInteger _lastDynamicId = new AtomicInteger(300000); private List _debugItems; - private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.MAP_MAX_X >> SHIFT_BY) + OFFSET_X + 1][(World.MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y + 1]; + private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.WORLD_X_MAX >> SHIFT_BY) + OFFSET_X + 1][(World.WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y + 1]; /** * Instantiates a new zone manager. diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/Location.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/Location.java index 73689ab1a6..ca3b22a3f0 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/Location.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/Location.java @@ -16,30 +16,30 @@ */ package org.l2jmobius.gameserver.model; +import java.util.Objects; + +import org.l2jmobius.commons.util.Point2D; import org.l2jmobius.gameserver.model.interfaces.ILocational; import org.l2jmobius.gameserver.model.interfaces.IPositionable; /** - * Location data transfer object.
- * Contains coordinates data, heading and instance Id. - * @author Zoey76 + * A datatype used to retain a 3D (x/y/z/heading) point. It got the capability to be set and cleaned. */ -public class Location implements IPositionable +public class Location extends Point2D implements IPositionable { - protected int _x; - protected int _y; - protected int _z; - private int _heading; + protected volatile int _z; + protected volatile int _heading; public Location(int x, int y, int z) { - this(x, y, z, 0); + super(x, y); + _z = z; + _heading = 0; } public Location(int x, int y, int z, int heading) { - _x = x; - _y = y; + super(x, y); _z = z; _heading = heading; } @@ -51,9 +51,8 @@ public class Location implements IPositionable public Location(StatSet set) { - _x = set.getInt("x"); - _y = set.getInt("y"); - _z = set.getInt("z"); + super(set.getInt("x", 0), set.getInt("y", 0)); + _z = set.getInt("z", 0); _heading = set.getInt("heading", 0); } @@ -146,6 +145,25 @@ public class Location implements IPositionable _heading = loc.getHeading(); } + @Override + public void clean() + { + super.clean(); + _z = 0; + } + + @Override + public Location clone() + { + return new Location(_x, _y, _z); + } + + @Override + public int hashCode() + { + return (31 * super.hashCode()) + Objects.hash(_z); + } + @Override public boolean equals(Object obj) { diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/World.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/World.java index c1510e8c0e..0d796dd834 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/World.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/World.java @@ -66,19 +66,19 @@ public class World public static final int TILE_Y_MAX = 26; public static final int TILE_ZERO_COORD_X = 20; public static final int TILE_ZERO_COORD_Y = 18; - public static final int MAP_MIN_X = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; - public static final int MAP_MIN_Y = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; + public static final int WORLD_X_MIN = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; + public static final int WORLD_Y_MIN = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; - public static final int MAP_MAX_X = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; - public static final int MAP_MAX_Y = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; + public static final int WORLD_X_MAX = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; + public static final int WORLD_Y_MAX = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; /** Calculated offset used so top left region is 0,0 */ - public static final int OFFSET_X = Math.abs(MAP_MIN_X >> SHIFT_BY); - public static final int OFFSET_Y = Math.abs(MAP_MIN_Y >> SHIFT_BY); + public static final int OFFSET_X = Math.abs(WORLD_X_MIN >> SHIFT_BY); + public static final int OFFSET_Y = Math.abs(WORLD_Y_MIN >> SHIFT_BY); /** Number of regions. */ - private static final int REGIONS_X = (MAP_MAX_X >> SHIFT_BY) + OFFSET_X; - private static final int REGIONS_Y = (MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y; + private static final int REGIONS_X = (WORLD_X_MAX >> SHIFT_BY) + OFFSET_X; + private static final int REGIONS_Y = (WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y; /** Map containing all the players in game. */ private static final Map _allPlayers = new ConcurrentHashMap<>(); @@ -817,9 +817,6 @@ public class World return _memberInPartyNumber.get(); } - /** - * @return the current instance of World - */ public static World getInstance() { return SingletonHolder.INSTANCE; diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/WorldObject.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/WorldObject.java index 0e5536bbc0..62313ffea1 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/WorldObject.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/WorldObject.java @@ -188,23 +188,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif synchronized (this) { int spawnX = x; - if (spawnX > World.MAP_MAX_X) + if (spawnX > World.WORLD_X_MAX) { - spawnX = World.MAP_MAX_X - 5000; + spawnX = World.WORLD_X_MAX - 5000; } - if (spawnX < World.MAP_MIN_X) + if (spawnX < World.WORLD_X_MIN) { - spawnX = World.MAP_MIN_X + 5000; + spawnX = World.WORLD_X_MIN + 5000; } int spawnY = y; - if (spawnY > World.MAP_MAX_Y) + if (spawnY > World.WORLD_Y_MAX) { - spawnY = World.MAP_MAX_Y - 5000; + spawnY = World.WORLD_Y_MAX - 5000; } - if (spawnY < World.MAP_MIN_Y) + if (spawnY < World.WORLD_Y_MIN) { - spawnY = World.MAP_MIN_Y + 5000; + spawnY = World.WORLD_Y_MIN + 5000; } // Set the x,y,z position of the WorldObject. If flagged with _isSpawned, setXYZ will automatically update world region, so avoid that. @@ -518,23 +518,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif public void setXYZInvisible(int x, int y, int z) { int correctX = x; - if (correctX > World.MAP_MAX_X) + if (correctX > World.WORLD_X_MAX) { - correctX = World.MAP_MAX_X - 5000; + correctX = World.WORLD_X_MAX - 5000; } - if (correctX < World.MAP_MIN_X) + if (correctX < World.WORLD_X_MIN) { - correctX = World.MAP_MIN_X + 5000; + correctX = World.WORLD_X_MIN + 5000; } int correctY = y; - if (correctY > World.MAP_MAX_Y) + if (correctY > World.WORLD_Y_MAX) { - correctY = World.MAP_MAX_Y - 5000; + correctY = World.WORLD_Y_MAX - 5000; } - if (correctY < World.MAP_MIN_Y) + if (correctY < World.WORLD_Y_MIN) { - correctY = World.MAP_MIN_Y + 5000; + correctY = World.WORLD_Y_MIN + 5000; } setXYZ(correctX, correctY, z); diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/actor/Creature.java index c1f646d842..aef8805a04 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -65,8 +65,6 @@ 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.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -2585,7 +2583,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3406,8 +3404,8 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe final int originalX = x; final int originalY = y; final int originalZ = z; - final int gtx = (originalX - World.MAP_MIN_X) >> 4; - final int gty = (originalY - World.MAP_MIN_Y) >> 4; + final int gtx = (originalX - World.WORLD_X_MIN) >> 4; + final int gty = (originalY - World.WORLD_Y_MIN) >> 4; if (isOnGeodataPath()) { try @@ -3430,7 +3428,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe && !(((curZ - z) > 300) && (distance < 300))) // Prohibit correcting destination if character wants to fall. { // location different if destination wasn't reached (or just z coord is different) - final Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(curX, curY, curZ, x, y, z, getInstanceWorld()); + final Location destiny = GeoEngine.getInstance().getValidLocation(curX, curY, curZ, x, y, z, getInstanceWorld()); x = destiny.getX(); y = destiny.getY(); z = destiny.getZ(); @@ -3444,7 +3442,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { if (isPlayer() && !_isFlying && !isInWater) diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/actor/Summon.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/actor/Summon.java index 6065b24f61..919cda941e 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/actor/Summon.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/actor/Summon.java @@ -106,7 +106,7 @@ public abstract class Summon extends Playable final int x = owner.getX(); final int y = owner.getY(); final int z = owner.getZ(); - final Location location = GeoEngine.getInstance().canMoveToTargetLoc(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, owner.getInstanceWorld()); + final Location location = GeoEngine.getInstance().getValidLocation(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, getInstanceWorld()); setXYZInvisible(location.getX(), location.getY(), location.getZ()); } diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java index 71b6dde756..c3b6b052fc 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java @@ -1507,7 +1507,7 @@ public class ItemInstance extends WorldObject if (dropper != null) { final Instance instance = dropper.getInstanceWorld(); - final Location dropDest = GeoEngine.getInstance().canMoveToTargetLoc(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); + final Location dropDest = GeoEngine.getInstance().getValidLocation(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); x = dropDest.getX(); y = dropDest.getY(); z = dropDest.getZ(); diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java index 4771ad5957..ef1fd785d7 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java @@ -1179,7 +1179,7 @@ public class SkillCaster implements Runnable } } - final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().canMoveToTargetLoc(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); + final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().getValidLocation(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); creature.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); creature.broadcastPacket(new FlyToLocation(creature, destination, flyType, 0, 0, 333)); diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java index c62c821f44..30068134e3 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java @@ -114,7 +114,7 @@ public class ValidatePosition implements IClientIncomingPacket { if (player.isFalling(_z)) { - final int nearestZ = GeoEngine.getInstance().getHigherHeight(_x, _y, _z); + final int nearestZ = GeoEngine.getInstance().getHeight(_x, _y, _z); if (player.getZ() < nearestZ) { player.setXYZ(_x, _y, nearestZ); diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/util/GeoUtils.java index 4600a0fc53..19a979819e 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -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.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; @@ -30,21 +30,21 @@ public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getInstance().getWorldX(iter.x()); - final int wy = GeoEngine.getInstance().getWorldY(iter.y()); + final int wx = GeoEngine.getWorldX(iter.x()); + final int wy = GeoEngine.getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public final class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); - int wy = GeoEngine.getInstance().getWorldY(prevY); + int wx = GeoEngine.getWorldX(prevX); + int wy = GeoEngine.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.getInstance().getWorldX(curX); - wy = GeoEngine.getInstance().getWorldY(curY); + wx = GeoEngine.getWorldX(curX); + wy = GeoEngine.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().checkNearestNswe(x, y, z, nswe)) + if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0) { return Color.GREEN; } @@ -109,9 +109,8 @@ public final class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final GeoEngine ge = GeoEngine.getInstance(); - final int playerGx = ge.getGeoX(player.getX()); - final int playerGy = ge.getGeoY(player.getY()); + final int playerGx = GeoEngine.getGeoX(player.getX()); + final int playerGy = GeoEngine.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -135,32 +134,32 @@ public final class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = ge.getWorldX(gx); - final int y = ge.getWorldY(gy); - final int z = ge.getNearestZ(gx, gy, player.getZ()); + final int x = GeoEngine.getWorldX(gx); + final int y = GeoEngine.getWorldY(gy); + final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); + Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); 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, Cell.NSWE_EAST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); 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, Cell.NSWE_SOUTH); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); 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, Cell.NSWE_WEST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); 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); @@ -188,41 +187,41 @@ public final class GeoUtils { if (y > lastY) { - return Cell.NSWE_SOUTH_EAST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E; } else if (y < lastY) { - return Cell.NSWE_NORTH_EAST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E; } else { - return Cell.NSWE_EAST; + return GeoStructure.CELL_FLAG_E; } } else if (x < lastX) // west { if (y > lastY) { - return Cell.NSWE_SOUTH_WEST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W; } else if (y < lastY) { - return Cell.NSWE_NORTH_WEST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W; } else { - return Cell.NSWE_WEST; + return GeoStructure.CELL_FLAG_W; } } else // unchanged x { if (y > lastY) { - return Cell.NSWE_SOUTH; + return GeoStructure.CELL_FLAG_S; } else if (y < lastY) { - return Cell.NSWE_NORTH; + return GeoStructure.CELL_FLAG_N; } else { diff --git a/L2J_Mobius_3.0_Helios/dist/game/config/GeoEngine.ini b/L2J_Mobius_3.0_Helios/dist/game/config/GeoEngine.ini index 609e8a89a7..e740c678dd 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_3.0_Helios/dist/game/config/GeoEngine.ini @@ -6,33 +6,44 @@ # at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ 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 + # ================================================================= -# Path finding +# Pathfinding # ================================================================= # 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 +# an alternative path (e.g. walk around obstacle), default: True +PathFinding = True -# Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 -PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 +# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 -# Weight for nodes without obstacles far from walls -LowWeight = 0.5 +# Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 +MoveWeight = 10 +MoveWeightDiag = 14 -# Weight for nodes near walls -MediumWeight = 2 +# 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 -# Weight for nodes with obstacles -HighWeight = 3 +# 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. -# Default: LowWeight * sqrt(2) -DiagonalWeight = 0.707 +# Maximum number of generated nodes per one path-finding process, default 3500 +MaxIterations = 3500 # ================================================================= -# Other +# Line of Sight # ================================================================= -# Correct player Z after falling through the ground. -CorrectPlayerZ = False +# 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 diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/geodata/L2D_GeoEngine.patch b/L2J_Mobius_3.0_Helios/dist/game/data/geodata/L2D_GeoEngine.patch deleted file mode 100644 index f8577e3a0e..0000000000 --- a/L2J_Mobius_3.0_Helios/dist/game/data/geodata/L2D_GeoEngine.patch +++ /dev/null @@ -1,6035 +0,0 @@ -Index: dist/game/GeoDataConverter.bat -=================================================================== ---- dist/game/GeoDataConverter.bat (nonexistent) -+++ dist/game/GeoDataConverter.bat (working copy) -@@ -0,0 +1,6 @@ -+@echo off -+title L2D geodata converter -+ -+java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter -+ -+pause -Index: dist/game/GeoDataConverter.sh -=================================================================== ---- dist/game/GeoDataConverter.sh (nonexistent) -+++ dist/game/GeoDataConverter.sh (working copy) -@@ -0,0 +1,4 @@ -+#! /bin/sh -+ -+java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 -+ -Index: dist/game/config/GeoEngine.ini -=================================================================== ---- dist/game/config/GeoEngine.ini (revision 8409) -+++ dist/game/config/GeoEngine.ini (working copy) -@@ -1,6 +1,10 @@ - # ================================================================= - # Geodata - # ================================================================= -+# Because of real-time performance we are using geodata files only in -+# diagonal L2D format now (using filename e.g. 22_16.l2d). -+# L2D geodata can be obtained by conversion of existing L2J or L2OFF geodata. -+# Launch "GeoDataConverter.bat/sh" and follow instructions to start the conversion. - - # 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/ -@@ -13,6 +17,16 @@ - CoordSynchronize = 2 - - # ================================================================= -+# Path checking -+# ================================================================= -+ -+# 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 finding - # ================================================================= - -@@ -23,19 +37,23 @@ - # Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - --# Weight for nodes without obstacles far from walls --LowWeight = 0.5 -+# Base path weight, when moving from one node to another on axis direction, default: 10 -+BaseWeight = 10 - --# Weight for nodes near walls --MediumWeight = 2 -+# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -+DiagonalWeight = 14 - --# Weight for nodes with obstacles --HighWeight = 3 -+# When movement flags of target node is blocked to any direction, multiply movement weight by this multiplier. -+# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 10 -+ObstacleMultiplier = 10 - --# Weight for diagonal movement. --# Default: LowWeight * sqrt(2) --DiagonalWeight = 0.707 -+# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 20 -+# For proper function must be higher than BaseWeight and/or DiagonalWeight. -+HeuristicWeight = 20 - -+# Maximum number of generated nodes per one path-finding process, default 3500 -+MaxIterations = 3500 -+ - # ================================================================= - # Other - # ================================================================= -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (working copy) -@@ -55,9 +55,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -@@ -73,9 +72,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (working copy) -@@ -18,11 +18,11 @@ - - import java.util.List; - --import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -+import org.l2jmobius.gameserver.geoengine.GeoEngine; - import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -+import org.l2jmobius.gameserver.network.SystemMessageId; - import org.l2jmobius.gameserver.util.BuilderUtil; - - public class AdminPathNode implements IAdminCommandHandler -@@ -37,29 +37,30 @@ - { - if (command.equals("admin_path_find")) - { -- if (!Config.PATHFINDING) -- { -- BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); -- return true; -- } - if (activeChar.getTarget() != null) - { -- final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); -+ final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); - if (path == null) - { -- BuilderUtil.sendSysMessage(activeChar, "No Route!"); -- return true; -+ BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); - } -- for (AbstractNodeLoc a : path) -+ else - { -- BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); -+ for (Location point : path) -+ { -+ BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); -+ } - } - } - else - { -- BuilderUtil.sendSysMessage(activeChar, "No Target!"); -+ activeChar.sendPacket(SystemMessageId.INVALID_TARGET); - } - } -+ else -+ { -+ return false; -+ } - return true; - } - -Index: java/org/l2jmobius/Config.java -=================================================================== ---- java/org/l2jmobius/Config.java (revision 8409) -+++ java/org/l2jmobius/Config.java (working copy) -@@ -32,7 +32,6 @@ - import java.net.UnknownHostException; - import java.nio.charset.StandardCharsets; - import java.nio.file.Files; --import java.nio.file.Path; - import java.nio.file.Paths; - import java.time.Duration; - import java.util.ArrayList; -@@ -966,14 +965,17 @@ - // -------------------------------------------------- - // GeoEngine - // -------------------------------------------------- -- public static Path GEODATA_PATH; -+ public static String GEODATA_PATH; -+ public static int COORD_SYNCHRONIZE; -+ public static int PART_OF_CHARACTER_HEIGHT; -+ public static int MAX_OBSTACLE_HEIGHT; - public static boolean PATHFINDING; - public static String PATHFIND_BUFFERS; -- public static float LOW_WEIGHT; -- public static float MEDIUM_WEIGHT; -- public static float HIGH_WEIGHT; -- public static float DIAGONAL_WEIGHT; -- public static int COORD_SYNCHRONIZE; -+ public static int BASE_WEIGHT; -+ public static int DIAGONAL_WEIGHT; -+ public static int HEURISTIC_WEIGHT; -+ public static int OBSTACLE_MULTIPLIER; -+ public static int MAX_ITERATIONS; - public static boolean CORRECT_PLAYER_Z; - - /** Attribute System */ -@@ -2568,14 +2570,17 @@ - - // Load GeoEngine config file (if exists) - final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); -- GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); -+ GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); -+ COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); -+ MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); - PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -- LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); -- MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); -- HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); -- DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); -- COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); -+ DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); -+ OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); -+ HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); -+ MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); - CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); - - // Load AllowedPlayerRaces config file (if exists) -Index: java/org/l2jmobius/gameserver/geoengine/GeoEngine.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (revision 8435) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (working copy) -@@ -16,100 +16,90 @@ - */ - package org.l2jmobius.gameserver.geoengine; - --import java.io.IOException; -+import java.io.File; - import java.io.RandomAccessFile; - import java.nio.ByteOrder; --import java.nio.channels.FileChannel.MapMode; --import java.nio.file.Files; --import java.nio.file.Path; --import java.util.concurrent.atomic.AtomicReferenceArray; --import java.util.logging.Level; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.List; - import java.util.logging.Logger; - - import org.l2jmobius.Config; - import org.l2jmobius.gameserver.data.xml.DoorData; - import org.l2jmobius.gameserver.data.xml.FenceData; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; --import org.l2jmobius.gameserver.geoengine.geodata.IRegion; --import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; --import org.l2jmobius.gameserver.geoengine.geodata.Region; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.World; - import org.l2jmobius.gameserver.model.WorldObject; -+import org.l2jmobius.gameserver.model.actor.Creature; - import org.l2jmobius.gameserver.model.instancezone.Instance; --import org.l2jmobius.gameserver.model.interfaces.ILocational; --import org.l2jmobius.gameserver.util.GeoUtils; --import org.l2jmobius.gameserver.util.LinePointIterator; --import org.l2jmobius.gameserver.util.LinePointIterator3D; -+import org.l2jmobius.gameserver.util.MathUtil; - - /** -- * @author -Nemesiss-, HorridoJoho -+ * @author Hasha - */ - public class GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); -+ protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - -- private static final int WORLD_MIN_X = -655360; -- private static final int WORLD_MIN_Y = -589824; -- private static final int WORLD_MIN_Z = -16384; -+ private final ABlock[][] _blocks; -+ private final BlockNull _nullBlock; - -- /** 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; -- -- /** The regions array */ -- private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); -- -- private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; -- private static final int ELEVATED_SEE_OVER_DISTANCE = 2; -- private static final int MAX_SEE_OVER_HEIGHT = 48; -- -- protected GeoEngine() -+ /** -+ * GeoEngine constructor. Loads all geodata files of chosen geodata format. -+ */ -+ public GeoEngine() - { - LOGGER.info("GeoEngine: Initializing..."); -- for (int i = 0; i < _regions.length(); i++) -- { -- _regions.set(i, NullRegion.INSTANCE); -- } - -+ // initialize block container -+ _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; -+ -+ // load null block -+ _nullBlock = new BlockNull(); -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files according to geoengine config setup - int loaded = 0; -- try -+ long fileSize = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { -- for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { -- for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) -+ final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); -+ if (f.exists() && !f.isDirectory()) - { -- final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); -- if (Files.exists(geoFilePath)) -+ // region file is load-able, try to load it -+ if (loadGeoBlocks(rx, ry)) - { -- try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) -- { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -- loaded++; -- } -+ loaded++; -+ fileSize += f.length(); - } - } -+ else -+ { -+ // region file is not load-able, load null blocks -+ loadNullBlocks(rx, ry); -+ } - } - } -- catch (Exception e) -+ -+ LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -+ if (loaded > 0) - { -- LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); -- System.exit(1); -+ LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); - } - -- LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -- -- // Avoid wrong configs when no files are loaded. -+ // avoid wrong configs when no files are loaded - if (loaded == 0) - { - if (Config.PATHFINDING) -@@ -123,619 +113,820 @@ - LOGGER.info("GeoEngine: Forcing CoordSynchronize setting to -1."); - } - } -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); - } - - /** -- * @param geoX -- * @param geoY -- * @return the region -+ * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. -+ * @return boolean : True, when geodata file was loaded without problem. - */ -- private IRegion getRegion(int geoX, int geoY) -+ private final boolean loadGeoBlocks(int regionX, int regionY) - { -- final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); -- if ((region < 0) || (region >= _regions.length())) -+ final String filename = String.format(GeoFormat.L2D.getFilename(), regionX, regionY); -+ -+ // standard load -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) - { -- return null; -+ // initialize file buffer -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, GeoFormat.L2D); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ } -+ -+ // check data consistency -+ if (buffer.remaining() > 0) -+ { -+ LOGGER.warning("GeoEngine: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ } -+ -+ // loading was successful -+ return true; - } -- return _regions.get(region); -- } -- -- /** -- * @param filePath -- * @param regionX -- * @param regionY -- * @throws IOException -- */ -- 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")) -+ catch (Exception e) - { -- _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -+ // an error occured while loading, load null blocks -+ LOGGER.warning("GeoEngine: Error while loading " + filename + " region file."); -+ LOGGER.warning(e.getMessage()); -+ -+ // replace whole region file with null blocks -+ loadNullBlocks(regionX, regionY); -+ -+ // loading was not successful -+ return false; - } - } - - /** -- * @param regionX -- * @param regionY -+ * Loads null blocks. Used when no region file is detected or an error occurs during loading. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. - */ -- public void unloadRegion(int regionX, int regionY) -+ private final void loadNullBlocks(int regionX, int regionY) - { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); -- } -- -- /** -- * @param geoX -- * @param geoY -- * @return if geodata exist -- */ -- public boolean hasGeoPos(int geoX, int geoY) -- { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // load all null blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { -- return false; -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[blockX + ix][blockY + iy] = _nullBlock; -+ } - } -- return region.hasGeo(); - } - -+ // GEODATA - GENERAL -+ - /** -- * Checks the specified position for available geodata. -- * @param x the world x -- * @param y the world y -- * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise -+ * Converts world X to geodata X. -+ * @param worldX -+ * @return int : Geo X - */ -- public boolean hasGeo(int x, int y) -+ public static int getGeoX(int worldX) - { -- return hasGeoPos(getGeoX(x), getGeoY(y)); -+ return (MathUtil.limit(worldX, World.MAP_MIN_X, World.MAP_MAX_X) - World.MAP_MIN_X) >> 4; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe check -+ * Converts world Y to geodata Y. -+ * @param worldY -+ * @return int : Geo Y - */ -- public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) -+ public static int getGeoY(int worldY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return true; -- } -- return region.checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(worldY, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.MAP_MIN_Y) >> 4; - } - - /** -+ * Converts geodata X to world X. - * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe anti-corner cut -+ * @return int : World X - */ -- public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) -+ public static int getWorldX(int geoX) - { -- boolean can = true; -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- return can && checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(geoX, 0, GeoStructure.GEO_CELLS_X) << 4) + World.MAP_MIN_X + 8; - } - - /** -- * @param geoX -+ * Converts geodata Y to world Y. - * @param geoY -- * @param worldZ -- * @return the nearest Z value -+ * @return int : World Y - */ -- public int getNearestZ(int geoX, int geoY, int worldZ) -+ public static int getWorldY(int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return worldZ; -- } -- return region.getNearestZ(geoX, geoY, worldZ); -+ return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next lower Z value -+ * Returns block of geodata on given coordinates. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return {@link ABlock} : Block of geodata. - */ -- public int getNextLowerZ(int geoX, int geoY, int worldZ) -+ private final ABlock getBlock(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final int x = geoX / GeoStructure.BLOCK_CELLS_X; -+ final int y = geoY / GeoStructure.BLOCK_CELLS_Y; -+ -+ // if x or y is out of array return null -+ if ((x > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) - { -- return worldZ; -+ return _blocks[x][y]; - } -- return region.getNextLowerZ(geoX, geoY, worldZ); -+ return null; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next higher Z value -+ * Check if geo coordinates has geo. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return boolean : True, if given geo coordinates have geodata - */ -- public int getNextHigherZ(int geoX, int geoY, int worldZ) -+ public boolean hasGeoPos(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final ABlock block = getBlock(geoX, geoY); -+ if (block == null) // null block check - { -- return worldZ; -+ // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) -+ // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); -+ return false; - } -- return region.getNextHigherZ(geoX, geoY, worldZ); -+ return block.hasGeoPos(); - } - - /** -- * Gets the Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the height of cell, which is closest to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, closest to given coordinates. - */ -- public int getHeight(int x, int y, int z) -+ public short getHeightNearest(int geoX, int geoY, int worldZ) - { -- return getNearestZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getHeightNearest(geoX, geoY, worldZ) : (short) worldZ; - } - - /** -- * Gets the next lower Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the NSWE flag byte of cell, which is closes to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. - */ -- public int getLowerHeight(int x, int y, int z) -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) - { -- return getNextLowerZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getNsweNearest(geoX, geoY, worldZ) : (byte) 0xFF; - } - - /** -- * Gets the next higher Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Check if world coordinates has geo. -+ * @param worldX : World X -+ * @param worldY : World Y -+ * @return boolean : True, if given world coordinates have geodata - */ -- public int getHigherHeight(int x, int y, int z) -+ public boolean hasGeo(int worldX, int worldY) - { -- return getNextHigherZ(getGeoX(x), getGeoY(y), z); -+ return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); - } - - /** -- * @param worldX -- * @return the geo X -+ * Returns closest Z coordinate according to geodata. -+ * @param worldX : world x -+ * @param worldY : world y -+ * @param worldZ : world z -+ * @return short : nearest Z coordinates according to geodata - */ -- public int getGeoX(int worldX) -+ public short getHeight(int worldX, int worldY, int worldZ) - { -- return (worldX - WORLD_MIN_X) / 16; -+ return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - -- /** -- * @param worldY -- * @return the geo Y -- */ -- public int getGeoY(int worldY) -- { -- return (worldY - WORLD_MIN_Y) / 16; -- } -+ // PATHFINDING - - /** -- * @param worldZ -- * @return the geo Z -+ * Check line of sight from {@link WorldObject} to {@link WorldObject}. -+ * @param origin : The origin object. -+ * @param target : The target object. -+ * @return {@code boolean} : True if origin can see target - */ -- public int getGeoZ(int worldZ) -+ public boolean canSeeTarget(WorldObject origin, WorldObject target) - { -- return (worldZ - WORLD_MIN_Z) / 16; -- } -- -- /** -- * @param geoX -- * @return the world X -- */ -- public int getWorldX(int geoX) -- { -- return (geoX * 16) + WORLD_MIN_X + 8; -- } -- -- /** -- * @param geoY -- * @return the world Y -- */ -- public int getWorldY(int geoY) -- { -- return (geoY * 16) + WORLD_MIN_Y + 8; -- } -- -- /** -- * @param geoZ -- * @return the world Z -- */ -- public int getWorldZ(int geoZ) -- { -- return (geoZ * 16) + WORLD_MIN_Z + 8; -- } -- -- /** -- * Can see target. Doors as target always return true. Checks doors between. -- * @param cha the character -- * @param target the target -- * @return {@code true} if the character can see the target (LOS), {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, WorldObject target) -- { -- if (target.isDoor()) -+ if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) - { -- // Can always see doors. - return true; - } -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param cha the character -- * @param worldPosition the world position -- * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) -- { -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param world -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @param tworld the target's instanceId -- * @return -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) -- { -- return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instance -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) -- { -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) -+ -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = target.getX(); -+ final int ty = target.getY(); -+ final int tz = target.getZ(); -+ -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } -- return canSeeTarget(x, y, z, tx, ty, tz); -- } -- -- /** -- * @param prevX -- * @param prevY -- * @param prevGeoZ -- * @param curX -- * @param curY -- * @param nswe -- * @return the LOS Z value -- */ -- private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) -- { -- if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { -- throw new RuntimeException("Multiple directions!"); -+ return false; - } - -- if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- return getNearestZ(curX, curY, prevGeoZ); -+ return true; - } - -- return getNextHigherZ(curX, curY, prevGeoZ); -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) -+ { -+ return true; -+ } -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) -+ { -+ return goz == gtz; -+ } -+ -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) -+ { -+ oheight = ((Creature) origin).getCollisionHeight() * 2; -+ } -+ -+ double theight = 0; -+ if (target.isCreature()) -+ { -+ theight = ((Creature) target).getCollisionHeight() * 2; -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); - } - - /** -- * Can see target. Does not check doors between. -- * @param xValue the x coordinate -- * @param yValue the y coordinate -- * @param zValue the z coordinate -- * @param txValue the target's x coordinate -- * @param tyValue the target's y coordinate -- * @param tzValue the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -+ * Check line of sight from {@link WorldObject} to {@link Location}. -+ * @param origin : The origin object. -+ * @param position : The target position. -+ * @return {@code boolean} : True if object can see position - */ -- public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) -+ public boolean canSeeTarget(WorldObject origin, Location position) - { -- int x = xValue; -- int y = yValue; -- int tx = txValue; -- int ty = tyValue; -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = position.getX(); -+ final int ty = position.getY(); -+ final int tz = position.getZ(); - -- int geoX = getGeoX(x); -- int geoY = getGeoY(y); -- int tGeoX = getGeoX(tx); -- int tGeoY = getGeoY(ty); -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) -+ { -+ return false; -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) -+ { -+ return false; -+ } - -- int z = getNearestZ(geoX, geoY, zValue); -- int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- // Fastpath. -- if ((geoX == tGeoX) && (geoY == tGeoY)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- if (hasGeoPos(tGeoX, tGeoY)) -- { -- return z == tz; -- } - return true; - } - -- if (tz > z) -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) - { -- int tmp = tx; -- tx = x; -- x = tmp; -- -- tmp = ty; -- ty = y; -- y = tmp; -- -- tmp = tz; -- tz = z; -- z = tmp; -- -- tmp = tGeoX; -- tGeoX = geoX; -- geoX = tmp; -- -- tmp = tGeoY; -- tGeoY = geoY; -- geoY = tmp; -+ return goz == gtz; - } - -- final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); -- // First point is guaranteed to be available, skip it, we can always see our own position. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- final int prevZ = pointIter.z(); -- int prevGeoZ = prevZ; -- int ptIndex = 0; -- while (pointIter.next()) -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight(); -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); -+ } -+ -+ /** -+ * Simple check for origin to target visibility. -+ * @param goxValue : origin X geodata coordinate -+ * @param goyValue : origin Y geodata coordinate -+ * @param gozValue : origin Z geodata coordinate -+ * @param oheight : origin height (if instance of {@link Character}) -+ * @param gtxValue : target X geodata coordinate -+ * @param gtyValue : target Y geodata coordinate -+ * @param gtzValue : target Z geodata coordinate -+ * @param theight : target height (if instance of {@link Character}) -+ * @param instance -+ * @return {@code boolean} : True, when target can be seen. -+ */ -+ private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) -+ { -+ int goz = gozValue; -+ int gtz = gtzValue; -+ int gox = goxValue; -+ int goy = goyValue; -+ int gtx = gtxValue; -+ int gty = gtyValue; -+ -+ // get line of sight Z coordinates -+ double losoz = goz + ((oheight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ double lostz = gtz + ((theight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ -+ // get X delta and signum -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirox = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; -+ final byte dirtx = sx > 0 ? GeoStructure.CELL_FLAG_W : GeoStructure.CELL_FLAG_E; -+ -+ // get Y delta and signum -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte diroy = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ final byte dirty = sy > 0 ? GeoStructure.CELL_FLAG_N : GeoStructure.CELL_FLAG_S; -+ -+ // get Z delta -+ final int dm = Math.max(dx, dy); -+ final double dz = (lostz - losoz) / dm; -+ -+ // get direction flag for diagonal movement -+ final byte diroxy = getDirXY(dirox, diroy); -+ final byte dirtxy = getDirXY(dirtx, dirty); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte diro; -+ byte dirt; -+ -+ // initialize node values -+ int nox = gox; -+ int noy = goy; -+ int ntx = gtx; -+ int nty = gty; -+ byte nsweo = getNsweNearest(gox, goy, goz); -+ byte nswet = getNsweNearest(gtx, gty, gtz); -+ -+ // loop -+ ABlock block; -+ int index; -+ for (int i = 0; i < ((dm + 1) / 2); i++) -+ { -+ // reset direction flag -+ diro = 0; -+ dirt = 0; - -- if ((curX == prevX) && (curY == prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- continue; -+ // calculate next point XY coordinates -+ d -= dy; -+ d += dx; -+ nox += sx; -+ ntx -= sx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroxy; -+ dirt |= dirtxy; - } -+ else if (e2 > -dy) -+ { -+ // calculate next point X coordinate -+ d -= dy; -+ nox += sx; -+ ntx -= sx; -+ diro |= dirox; -+ dirt |= dirtx; -+ } -+ else if (e2 < dx) -+ { -+ // calculate next point Y coordinate -+ d += dx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroy; -+ dirt |= dirty; -+ } - -- final int beeCurZ = pointIter.z(); -- int curGeoZ = prevGeoZ; -- -- // Check if the position has geodata. -- if (hasGeoPos(curX, curY)) - { -- final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); -- curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); -- int maxHeight; -- if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) -+ // get block of the next cell -+ block = getBlock(nox, noy); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nsweo & diro) == 0) - { -- maxHeight = z + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); - } - else - { -- maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); - } - -- boolean canSeeThrough = false; -- if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) -+ // layer does not exist, return -+ if (index == -1) - { -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else -- { -- canSeeThrough = true; -- } -+ return false; - } - -- if (!canSeeThrough) -+ // get layer and next line of sight Z coordinate -+ goz = block.getHeight(index); -+ losoz += dz; -+ -+ // perform line of sight check, return when fails -+ if ((goz - losoz) > Config.MAX_OBSTACLE_HEIGHT) - { - return false; - } -+ -+ // get layer nswe -+ nsweo = block.getNswe(index); - } -+ { -+ // get block of the next cell -+ block = getBlock(ntx, nty); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nswet & dirt) == 0) -+ { -+ index = block.getIndexAbove(ntx, nty, gtz - GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ else -+ { -+ index = block.getIndexBelow(ntx, nty, gtz + GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ -+ // layer does not exist, return -+ if (index == -1) -+ { -+ return false; -+ } -+ -+ // get layer and next line of sight Z coordinate -+ gtz = block.getHeight(index); -+ lostz -= dz; -+ -+ // perform line of sight check, return when fails -+ if ((gtz - lostz) > Config.MAX_OBSTACLE_HEIGHT) -+ { -+ return false; -+ } -+ -+ // get layer nswe -+ nswet = block.getNswe(index); -+ } - -- prevX = curX; -- prevY = curY; -- prevGeoZ = curGeoZ; -- ++ptIndex; -+ // update coords -+ gox = nox; -+ goy = noy; -+ gtx = ntx; -+ gty = nty; - } - -- return true; -+ // when iteration is completed, compare final Z coordinates -+ return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); - } - - /** -- * Move check. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param zValue the z coordinate -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tzValue the target's z coordinate -- * @param instance the instance -- * @return the last Location (x,y,z) where player can walk - just before wall -+ * Check movement from coordinates to coordinates. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {code boolean} : True if target coordinates are reachable from origin coordinates - */ -- public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) -+ public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- final int geoX = getGeoX(x); -- final int geoY = getGeoY(y); -- final int z = getNearestZ(geoX, geoY, zValue); -- final int tGeoX = getGeoX(tx); -- final int tGeoY = getGeoY(ty); -- final int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } -- if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = z; -+ // perform geodata check -+ final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); -+ return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); -+ } -+ -+ /** -+ * Check movement from origin to target. Returns last available point in the checked path. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {@link Location} : Last point where object can walk (just before wall) -+ */ -+ public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) -+ { -+ return new Location(ox, oy, oz); -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) -+ { -+ return new Location(ox, oy, oz); -+ } - -- while (pointIter.next()) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -- -- if (hasGeoPos(prevX, prevY)) -- { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -- { -- // Can't move, return previous location. -- return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); -- } -- } -- -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return new Location(tx, ty, tz); - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != tz)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- // Different floors, return start location. -- return new Location(x, y, z); -+ return new Location(tx, ty, tz); - } - -- return new Location(tx, ty, tz); -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) -+ { -+ return new Location(tx, ty, tz); -+ } -+ -+ // perform geodata check -+ return checkMove(gox, goy, goz, gtx, gty, gtz, instance); - } - - /** -- * Checks if its possible to move from one location to another. -- * @param fromX the X coordinate to start checking from -- * @param fromY the Y coordinate to start checking from -- * @param fromZvalue the Z coordinate to start checking from -- * @param toX the X coordinate to end checking at -- * @param toY the Y coordinate to end checking at -- * @param toZvalue the Z coordinate to end checking at -- * @param instance the instance -- * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise -+ * With this method you can check if a position is visible or can be reached by beeline movement.
-+ * Target X and Y reachable and Z is on same floor: -+ *
    -+ *
  • Location of the target with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y reachable but Z is on another floor: -+ *
    -+ *
  • Location of the origin with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y not reachable: -+ *
    -+ *
  • Last accessible location in destination to target.
  • -+ *
-+ * @param gox : origin X geodata coordinate -+ * @param goy : origin Y geodata coordinate -+ * @param goz : origin Z geodata coordinate -+ * @param gtx : target X geodata coordinate -+ * @param gty : target Y geodata coordinate -+ * @param gtz : target Z geodata coordinate -+ * @param instance -+ * @return {@link GeoLocation} : The last allowed point of movement. - */ -- public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) -+ protected final GeoLocation checkMove(int gox, int goy, int goz, int gtx, int gty, int gtz, Instance instance) - { -- final int geoX = getGeoX(fromX); -- final int geoY = getGeoY(fromY); -- final int fromZ = getNearestZ(geoX, geoY, fromZvalue); -- final int tGeoX = getGeoX(toX); -- final int tGeoY = getGeoY(toY); -- final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); -- -- if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) -+ if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } -- if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) -+ if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = fromZ; -+ // get X delta, signum and direction flag -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirX = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; - -- while (pointIter.next()) -+ // get Y delta, signum and direction flag -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte dirY = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ -+ // get direction flag for diagonal movement -+ final byte dirXY = getDirXY(dirX, dirY); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte direction; -+ -+ // load pointer coordinates -+ int gpx = gox; -+ int gpy = goy; -+ int gpz = goz; -+ -+ // load next pointer -+ int nx = gpx; -+ int ny = gpy; -+ -+ // loop -+ int count = 0; -+ while (count++ < Config.MAX_ITERATIONS) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -+ direction = 0; - -- if (hasGeoPos(prevX, prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -+ d -= dy; -+ d += dx; -+ nx += sx; -+ ny += sy; -+ direction |= dirXY; -+ } -+ else if (e2 > -dy) -+ { -+ d -= dy; -+ nx += sx; -+ direction |= dirX; -+ } -+ else if (e2 < dx) -+ { -+ d += dx; -+ ny += sy; -+ direction |= dirY; -+ } -+ -+ // obstacle found, return -+ if ((getNsweNearest(gpx, gpy, gpz) & direction) == 0) -+ { -+ return new GeoLocation(gpx, gpy, gpz); -+ } -+ -+ // update pointer coordinates -+ gpx = nx; -+ gpy = ny; -+ gpz = getHeightNearest(nx, ny, gpz); -+ -+ // target coordinates reached -+ if ((gpx == gtx) && (gpy == gty)) -+ { -+ if (gpz == gtz) - { -- return false; -+ // path found, Z coordinates are okay, return target point -+ return new GeoLocation(gtx, gty, gtz); - } -+ -+ // path found, Z coordinates are not okay, return last good point -+ return new GeoLocation(gpx, gpy, gpz); - } -+ } -+ -+ return new GeoLocation(gox, goy, goz); -+ } -+ -+ /** -+ * Returns diagonal NSWE flag format of combined two NSWE flags. -+ * @param dirX : X direction NSWE flag -+ * @param dirY : Y direction NSWE flag -+ * @return byte : NSWE flag of combined direction -+ */ -+ private static byte getDirXY(byte dirX, byte dirY) -+ { -+ // check axis directions -+ if (dirY == GeoStructure.CELL_FLAG_N) -+ { -+ if (dirX == GeoStructure.CELL_FLAG_W) -+ { -+ return GeoStructure.CELL_FLAG_NW; -+ } - -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return GeoStructure.CELL_FLAG_NE; - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) -+ if (dirX == GeoStructure.CELL_FLAG_W) - { -- // Different floors. -- return false; -+ return GeoStructure.CELL_FLAG_SW; - } - -- return true; -+ return GeoStructure.CELL_FLAG_SE; - } - -+ /** -+ * Returns the list of location objects as a result of complete path calculation. -+ * @param ox : origin x -+ * @param oy : origin y -+ * @param oz : origin z -+ * @param tx : target x -+ * @param ty : target y -+ * @param tz : target z -+ * @param instance -+ * @return {@code List} : complete path from nodes -+ */ -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ return null; -+ } -+ -+ /** -+ * Returns the instance of the {@link GeoEngine}. -+ * @return {@link GeoEngine} : The instance. -+ */ - public static GeoEngine getInstance() - { - return SingletonHolder.INSTANCE; -@@ -743,6 +934,6 @@ - - private static class SingletonHolder - { -- protected static final GeoEngine INSTANCE = new GeoEngine(); -+ protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); - } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (working copy) -@@ -20,88 +20,84 @@ - import java.util.LinkedList; - import java.util.List; - import java.util.ListIterator; --import java.util.logging.Level; --import java.util.logging.Logger; - - import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; --import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; --import org.l2jmobius.gameserver.model.World; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -+import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.instancezone.Instance; - - /** -- * @author -Nemesiss- -+ * @author Hasha - */ --public class GeoEnginePathfinding -+final class GeoEnginePathfinding extends GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); -+ // pre-allocated buffers -+ private final BufferHolder[] _buffers; - -- private BufferInfo[] _buffers; -- - protected GeoEnginePathfinding() - { -- try -+ super(); -+ -+ final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ _buffers = new BufferHolder[array.length]; -+ int count = 0; -+ for (int i = 0; i < array.length; i++) - { -- final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ final String buf = array[i]; -+ final String[] args = buf.split("x"); - -- _buffers = new BufferInfo[array.length]; -- -- String buf; -- String[] args; -- for (int i = 0; i < array.length; i++) -+ try - { -- buf = array[i]; -- args = buf.split("x"); -- if (args.length != 2) -- { -- throw new Exception("Invalid buffer definition: " + buf); -- } -- -- _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); -+ final int size = Integer.parseInt(args[1]); -+ count += size; -+ _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); - } -+ catch (Exception e) -+ { -+ LOGGER.warning("GeoEnginePathfinding: Can not load buffer setting: " + buf); -+ } - } -- catch (Exception e) -- { -- LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); -- throw new Error("CellPathFinding: load aborted"); -- } -+ -+ LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); - } - -- public boolean pathNodesExist(short regionoffset) -+ @Override -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- return false; -- } -- -- public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) -- { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -- if (!GeoEngine.getInstance().hasGeo(x, y)) -+ // get origin and check existing geo coords -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { - 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)) -+ -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coords -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { - 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)))); -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // Prepare buffer for pathfinding calculations -+ final NodeBuffer buffer = getBuffer(64 + (2 * Math.max(Math.abs(gox - gtx), Math.abs(goy - gty)))); - if (buffer == null) - { - return null; - } - -- List path = null; -+ // find path -+ List path = null; - try - { -- final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); -- -+ final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); - if (result == null) - { - return null; -@@ -125,33 +121,45 @@ - return path; - } - -- int currentX, currentY, currentZ; -- ListIterator middlePoint; -+ // get path list iterator -+ final ListIterator point = path.listIterator(); - -- middlePoint = path.listIterator(); -- currentX = x; -- currentY = y; -- currentZ = z; -+ // get node A (origin) -+ int nodeAx = gox; -+ int nodeAy = goy; -+ short nodeAz = goz; - -- while (middlePoint.hasNext()) -+ // get node B -+ GeoLocation nodeB = (GeoLocation) point.next(); -+ -+ // iterate thought the path to optimize it -+ int count = 0; -+ while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) - { -- final AbstractNodeLoc locMiddle = middlePoint.next(); -- if (!middlePoint.hasNext()) -- { -- break; -- } -+ // get node C -+ final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - -- final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); -- if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) -+ // check movement from node A to node C -+ final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); -+ if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) - { -- middlePoint.remove(); -+ // can move from node A to node C -+ -+ // remove node B -+ point.remove(); - } - else - { -- currentX = locMiddle.getX(); -- currentY = locMiddle.getY(); -- currentZ = locMiddle.getZ(); -+ // can not move from node A to node C -+ -+ // set node A (node B is part of path, update A coordinates) -+ nodeAx = nodeB.getGeoX(); -+ nodeAy = nodeB.getGeoY(); -+ nodeAz = (short) nodeB.getZ(); - } -+ -+ // set node B -+ nodeB = (GeoLocation) point.next(); - } - - return path; -@@ -158,144 +166,102 @@ - } - - /** -- * Convert geodata position to pathnode position -- * @param geo_pos -- * @return pathnode position -+ * Create list of node locations as result of calculated buffer node tree. -+ * @param node : the entry point -+ * @return List : list of node location - */ -- public short getNodePos(int geo_pos) -+ private static List constructPath(Node node) - { -- return (short) (geo_pos >> 3); // OK? -- } -- -- /** -- * Convert node position to pathnode block position -- * @param node_pos -- * @return pathnode block position (0...255) -- */ -- public short getNodeBlock(int node_pos) -- { -- return (short) (node_pos % 256); -- } -- -- public byte getRegionX(int node_pos) -- { -- return (byte) ((node_pos >> 8) + World.TILE_X_MIN); -- } -- -- public byte getRegionY(int node_pos) -- { -- return (byte) ((node_pos >> 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 node_x rx -- * @return -- */ -- public int calculateWorldX(short node_x) -- { -- return World.MAP_MIN_X + (node_x * 128) + 48; -- } -- -- /** -- * Convert pathnode y to World y position -- * @param node_y -- * @return -- */ -- public int calculateWorldY(short node_y) -- { -- return World.MAP_MIN_Y + (node_y * 128) + 48; -- } -- -- private List constructPath(AbstractNode nodeValue) -- { -- final LinkedList path = new LinkedList<>(); -- int previousDirectionX = Integer.MIN_VALUE; -- int previousDirectionY = Integer.MIN_VALUE; -- int directionX, directionY; -+ // create empty list -+ final LinkedList list = new LinkedList<>(); - -- AbstractNode node = nodeValue; -- while (node.getParent() != null) -+ // set direction X/Y -+ int dx = 0; -+ int dy = 0; -+ -+ // get target parent -+ Node target = node; -+ Node parent = target.getParent(); -+ -+ // while parent exists -+ int count = 0; -+ while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { -- directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); -- directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); -+ // get parent <> target direction X/Y -+ final int nx = parent.getLoc().getGeoX() - target.getLoc().getGeoX(); -+ final int ny = parent.getLoc().getGeoY() - target.getLoc().getGeoY(); - -- // only add a new route point if moving direction changes -- if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) -+ // direction has changed? -+ if ((dx != nx) || (dy != ny)) - { -- previousDirectionX = directionX; -- previousDirectionY = directionY; -+ // add node to the beginning of the list -+ list.addFirst(target.getLoc()); - -- path.addFirst(node.getLoc()); -- node.setLoc(null); -+ // update direction X/Y -+ dx = nx; -+ dy = ny; - } - -- node = node.getParent(); -+ // move to next node, set target and get its parent -+ target = parent; -+ parent = target.getParent(); - } - -- return path; -+ // return list -+ return list; - } - -- private CellNodeBuffer alloc(int size) -+ /** -+ * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer. -+ * @param size : pre-calculated minimal required size -+ * @return NodeBuffer : buffer -+ */ -+ private final NodeBuffer getBuffer(int size) - { -- CellNodeBuffer current = null; -- for (BufferInfo i : _buffers) -+ NodeBuffer current = null; -+ for (BufferHolder holder : _buffers) - { -- if (i.mapSize >= size) -+ // Find proper size of buffer -+ if (holder._size < size) - { -- for (CellNodeBuffer buf : i.buffer) -+ continue; -+ } -+ -+ // Find unlocked NodeBuffer -+ for (NodeBuffer buffer : holder._buffer) -+ { -+ if (!buffer.isLocked()) - { -- if (buf.lock()) -- { -- current = buf; -- break; -- } -+ continue; - } -- if (current != null) -- { -- break; -- } - -- // not found, allocate temporary buffer -- current = new CellNodeBuffer(i.mapSize); -- current.lock(); -- if (i.buffer.size() < i.count) -- { -- i.buffer.add(current); -- break; -- } -+ return buffer; - } -+ -+ // NodeBuffer not found, allocate temporary buffer -+ current = new NodeBuffer(holder._size); -+ current.isLocked(); - } - - return current; - } - -- private static final class BufferInfo -+ /** -+ * NodeBuffer container with specified size and count of separate buffers. -+ */ -+ private static final class BufferHolder - { -- final int mapSize; -- final int count; -- ArrayList buffer; -+ final int _size; -+ List _buffer; - -- public BufferInfo(int size, int cnt) -+ public BufferHolder(int size, int count) - { -- mapSize = size; -- count = cnt; -- buffer = new ArrayList<>(count); -+ _size = size; -+ _buffer = new ArrayList<>(count); -+ for (int i = 0; i < count; i++) -+ { -+ _buffer.add(new NodeBuffer(size)); -+ } - } - } -- -- public static GeoEnginePathfinding getInstance() -- { -- return SingletonHolder.INSTANCE; -- } -- -- private static class SingletonHolder -- { -- protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); -- } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (working copy) -@@ -0,0 +1,197 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @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 height of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getHeightNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first above given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, above given coordinates. -+ */ -+ public abstract short getHeightAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first below given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is closest to given coordinates. -+ * @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 the NSWE flag byte of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getNsweNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first above given coordinates. -+ * @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 getNsweAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first below given coordinates. -+ * @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 getNsweBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is closes layer to given coordinates. -+ * @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. -+ * @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 above given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is first layer below given coordinates. -+ * @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 index to data of the cell, which is first layer below given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexBelowOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeight(int index); -+ -+ /** -+ * Returns the height of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightOriginal(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNswe(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNsweOriginal(int index); -+ -+ /** -+ * Sets the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @param nswe : New NSWE flag byte. -+ */ -+ public abstract void setNswe(int index, byte nswe); -+ -+ /** -+ * Saves the block in L2D format to {@link BufferedOutputStream}. Used only for L2D geodata conversion. -+ * @param stream : The stream. -+ * @throws IOException : Can't save the block to steam. -+ */ -+ public abstract void saveBlock(BufferedOutputStream stream) throws IOException; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (working copy) -@@ -0,0 +1,252 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockComplex(ByteBuffer bb, GeoFormat format) -+ { -+ // initialize buffer -+ _buffer = new byte[GeoStructure.BLOCK_CELLS * 3]; -+ -+ // load data -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // 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); -+ } -+ else -+ { -+ // get nswe -+ final byte nswe = bb.get(); -+ _buffer[i * 3] = nswe; -+ -+ // get height -+ final short height = bb.getShort(); -+ _buffer[(i * 3) + 1] = (byte) (height & 0x00FF); -+ _buffer[(i * 3) + 2] = (byte) (height >> 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height > worldZ ? height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height < worldZ ? height : Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public 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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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 int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_COMPLEX_L2D); -+ -+ // write block data -+ stream.write(_buffer, 0, GeoStructure.BLOCK_CELLS * 3); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (working copy) -@@ -0,0 +1,176 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockFlat extends ABlock -+{ -+ protected final short _height; -+ protected byte _nswe; -+ -+ /** -+ * Creates FlatBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockFlat(ByteBuffer bb, GeoFormat format) -+ { -+ _height = bb.getShort(); -+ _nswe = format != GeoFormat.L2D ? 0x0F : (byte) (0xFF); -+ if (format == GeoFormat.L2OFF) -+ { -+ bb.getShort(); -+ } -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return true; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height > worldZ ? _height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height < worldZ ? _height : Short.MAX_VALUE; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height > worldZ ? _nswe : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height < worldZ ? _nswe : 0; -+ } -+ -+ @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return index -+ return _height < worldZ ? 0 : -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _nswe = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_FLAT_L2D); -+ -+ // write height -+ stream.write((byte) (_height & 0x00FF)); -+ stream.write((byte) (_height >> 8)); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (working copy) -@@ -0,0 +1,465 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+import java.nio.ByteOrder; -+import java.util.Arrays; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockMultilayer extends ABlock -+{ -+ private static final int MAX_LAYERS = Byte.MAX_VALUE; -+ -+ private static ByteBuffer _temp; -+ -+ /** -+ * Initializes the temporarily buffer. -+ */ -+ public static void initialize() -+ { -+ // initialize temporarily buffer and sorting mechanism -+ _temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3); -+ _temp.order(ByteOrder.LITTLE_ENDIAN); -+ } -+ -+ /** -+ * Releases temporarily buffer. -+ */ -+ public static void release() -+ { -+ _temp = null; -+ } -+ -+ protected byte[] _buffer; -+ -+ /** -+ * Implicit constructor for children class. -+ */ -+ protected BlockMultilayer() -+ { -+ _buffer = null; -+ } -+ -+ /** -+ * Creates MultilayerBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockMultilayer(ByteBuffer bb, GeoFormat format) -+ { -+ // 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 = format != GeoFormat.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++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // get data -+ final short data = bb.getShort(); -+ -+ // add nswe and height -+ _temp.put((byte) (data & 0x000F)); -+ _temp.putShort((short) ((short) (data & 0xFFF0) >> 1)); -+ } -+ else -+ { -+ // add nswe -+ _temp.put(bb.get()); -+ -+ // add height -+ _temp.putShort(bb.getShort()); -+ } -+ } -+ } -+ -+ // 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 height -+ if (height > worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, return minimum value -+ return Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 height -+ if (height < worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, return maximum value -+ return Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 nswe -+ if (height > worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 nswe -+ if (height < worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @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 first layer data (first from bottom) -+ byte layers = _buffer[index++]; -+ -+ // loop though all cell layers, find closest layer -+ 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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ // get height -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(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]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ // get nswe -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ // set nswe -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_MULTILAYER_L2D); -+ -+ // for each cell -+ int index = 0; -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ // write layers count -+ final byte layers = _buffer[index++]; -+ stream.write(layers); -+ -+ // write cell data -+ stream.write(_buffer, index, layers * 3); -+ -+ // move index to next cell -+ index += layers * 3; -+ } -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (working copy) -@@ -0,0 +1,150 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockNull extends ABlock -+{ -+ private final byte _nswe; -+ -+ public BlockNull() -+ { -+ _nswe = (byte) 0xFF; -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return false; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweBelow(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) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) -+ { -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (nonexistent) -@@ -1,48 +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 . -- */ --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() -- { -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (nonexistent) -@@ -1,77 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (nonexistent) -@@ -1,56 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (working copy) -@@ -0,0 +1,39 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public enum GeoFormat -+{ -+ L2J("%d_%d.l2j"), -+ L2OFF("%d_%d_conv.dat"), -+ L2D("%d_%d.l2d"); -+ -+ private final String _filename; -+ -+ private GeoFormat(String filename) -+ { -+ _filename = filename; -+ } -+ -+ public String getFilename() -+ { -+ return _filename; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (working copy) -@@ -0,0 +1,67 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.geoengine.GeoEngine; -+import org.l2jmobius.gameserver.model.Location; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoLocation extends Location -+{ -+ private byte _nswe; -+ -+ public GeoLocation(int x, int y, int z) -+ { -+ super(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public void set(int x, int y, short z) -+ { -+ super.setXYZ(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public int getGeoX() -+ { -+ return _x; -+ } -+ -+ public int getGeoY() -+ { -+ return _y; -+ } -+ -+ @Override -+ public int getX() -+ { -+ return GeoEngine.getWorldX(_x); -+ } -+ -+ @Override -+ public int getY() -+ { -+ return GeoEngine.getWorldY(_y); -+ } -+ -+ public byte getNSWE() -+ { -+ return _nswe; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (working copy) -@@ -0,0 +1,70 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoStructure -+{ -+ // cells -+ public static final byte CELL_FLAG_E = 1 << 0; -+ public static final byte CELL_FLAG_W = 1 << 1; -+ public static final byte CELL_FLAG_S = 1 << 2; -+ public static final byte CELL_FLAG_N = 1 << 3; -+ public static final byte CELL_FLAG_SE = 1 << 4; -+ public static final byte CELL_FLAG_SW = 1 << 5; -+ public static final byte CELL_FLAG_NE = 1 << 6; -+ public static final byte CELL_FLAG_NW = (byte) (1 << 7); -+ -+ public static final int CELL_HEIGHT = 8; -+ public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6; -+ -+ // blocks -+ public static final byte TYPE_FLAT_L2J_L2OFF = 0; -+ public static final byte TYPE_FLAT_L2D = (byte) 0xD0; -+ public static final byte TYPE_COMPLEX_L2J = 1; -+ public static final byte TYPE_COMPLEX_L2OFF = 0x40; -+ public static final byte TYPE_COMPLEX_L2D = (byte) 0xD1; -+ 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) -+ public static final byte TYPE_MULTILAYER_L2D = (byte) 0xD2; -+ -+ 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; -+ -+ // regions -+ 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; -+ -+ // global geodata -+ private static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); -+ private 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; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (nonexistent) -@@ -1,42 +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 . -- */ --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); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (working copy) -@@ -0,0 +1,53 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public interface IGeoObject -+{ -+ /** -+ * Returns geodata X coordinate of the {@link IGeoObject}. -+ * @return int : Geodata X coordinate. -+ */ -+ int getGeoX(); -+ -+ /** -+ * Returns geodata Y coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Y coordinate. -+ */ -+ int getGeoY(); -+ -+ /** -+ * Returns geodata Z coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Z coordinate. -+ */ -+ int getGeoZ(); -+ -+ /** -+ * Returns height of the {@link IGeoObject}. -+ * @return int : Height. -+ */ -+ int getHeight(); -+ -+ /** -+ * Returns {@link IGeoObject} data. -+ * @return byte[][] : {@link IGeoObject} data. -+ */ -+ byte[][] getObjectGeoData(); -+} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (nonexistent) -@@ -1,47 +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 . -- */ --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(); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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); -- -- // 1 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (nonexistent) -@@ -1,55 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --/** -- * @author HorridoJoho -- */ --public final class NullRegion implements IRegion --{ -- public static final NullRegion INSTANCE = new NullRegion(); -- -- @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 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Region.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (nonexistent) -@@ -1,92 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (nonexistent) -@@ -1,87 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public abstract class AbstractNode --{ -- private T _loc; -- private AbstractNode _parent; -- -- public AbstractNode(T loc) -- { -- _loc = loc; -- } -- -- public void setParent(AbstractNode p) -- { -- _parent = p; -- } -- -- public AbstractNode getParent() -- { -- return _parent; -- } -- -- public T getLoc() -- { -- return _loc; -- } -- -- public void setLoc(T l) -- { -- _loc = l; -- } -- -- @Override -- public int hashCode() -- { -- final int prime = 31; -- int result = 1; -- result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); -- return result; -- } -- -- @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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (nonexistent) -@@ -1,33 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --/** -- * @author -Nemesiss- -- */ --public abstract class AbstractNodeLoc --{ -- public abstract int getX(); -- -- public abstract int getY(); -- -- public abstract int getZ(); -- -- public abstract int getNodeX(); -- -- public abstract int getNodeY(); --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (nonexistent) -@@ -1,67 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public class CellNode extends AbstractNode --{ -- 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (nonexistent) -@@ -1,348 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --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 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) -- { -- _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(); -- } -- -- public final List debugPath() -- { -- final List 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 void getNeighbors() -- { -- if (!_current.getLoc().canGoAll()) -- { -- 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); -- } -- -- // SouthEast -- if ((nodeE != null) && (nodeS != null)) -- { -- if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) -- { -- addNode(x + 1, y + 1, z, true); -- } -- } -- -- // SouthWest -- if ((nodeS != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) -- { -- addNode(x - 1, y + 1, z, true); -- } -- } -- -- // NorthEast -- if ((nodeN != null) && (nodeE != null)) -- { -- if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) -- { -- addNode(x + 1, y - 1, z, true); -- } -- } -- -- // NorthWest -- if ((nodeN != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) -- { -- addNode(x - 1, y - 1, z, true); -- } -- } -- } -- -- private 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(); -- // reinit node if needed -- if (result.getLoc() != null) -- { -- result.getLoc().set(x, y, z); -- } -- else -- { -- result.setLoc(new NodeLoc(x, y, z)); -- } -- } -- -- return result; -- } -- -- private 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 boolean isHighWeight(int x, int y, int z) -- { -- final CellNode result = getNode(x, y, z); -- if (result == null) -- { -- return true; -- } -- -- if (!result.getLoc().canGoAll()) -- { -- return true; -- } -- if (Math.abs(result.getLoc().getZ() - z) > 16) -- { -- return true; -- } -- -- return false; -- } -- -- private 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (working copy) -@@ -0,0 +1,87 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+ -+/** -+ * @author Hasha -+ */ -+public class Node -+{ -+ // node coords and nswe flag -+ private GeoLocation _loc; -+ -+ // node parent (for reverse path construction) -+ private Node _parent; -+ // node child (for moving over nodes during iteration) -+ private Node _child; -+ -+ // node G cost (movement cost = parent movement cost + current movement cost) -+ private double _cost = -1000; -+ -+ public void setLoc(int x, int y, int z) -+ { -+ _loc = new GeoLocation(x, y, z); -+ } -+ -+ public GeoLocation getLoc() -+ { -+ return _loc; -+ } -+ -+ public void setParent(Node parent) -+ { -+ _parent = parent; -+ } -+ -+ public Node getParent() -+ { -+ return _parent; -+ } -+ -+ public void setChild(Node child) -+ { -+ _child = child; -+ } -+ -+ public Node getChild() -+ { -+ return _child; -+ } -+ -+ public void setCost(double cost) -+ { -+ _cost = cost; -+ } -+ -+ public double getCost() -+ { -+ return _cost; -+ } -+ -+ public void free() -+ { -+ // reset node location -+ _loc = null; -+ -+ // reset node parent, child and cost -+ _parent = null; -+ _child = null; -+ _cost = -1000; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (working copy) -@@ -0,0 +1,306 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import java.util.concurrent.locks.ReentrantLock; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+ -+/** -+ * @author DS, Hasha; Credits to Diamond -+ */ -+public class NodeBuffer -+{ -+ private final ReentrantLock _lock = new ReentrantLock(); -+ private final int _size; -+ private final Node[][] _buffer; -+ -+ // center coordinates -+ private int _cx = 0; -+ private int _cy = 0; -+ -+ // target coordinates -+ private int _gtx = 0; -+ private int _gty = 0; -+ private short _gtz = 0; -+ -+ private Node _current = null; -+ -+ /** -+ * Constructor of NodeBuffer. -+ * @param size : one dimension size of buffer -+ */ -+ public NodeBuffer(int size) -+ { -+ // set size -+ _size = size; -+ -+ // initialize buffer -+ _buffer = new Node[size][size]; -+ for (int x = 0; x < size; x++) -+ { -+ for (int y = 0; y < size; y++) -+ { -+ _buffer[x][y] = 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 Node : first node of path -+ */ -+ public Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) -+ { -+ // set coordinates (middle of the line (gox,goy) - (gtx,gty), will be in the center of the buffer) -+ _cx = gox + ((gtx - gox - _size) / 2); -+ _cy = goy + ((gty - goy - _size) / 2); -+ -+ _gtx = gtx; -+ _gty = gty; -+ _gtz = gtz; -+ -+ _current = getNode(gox, goy, goz); -+ _current.setCost(getCostH(gox, goy, goz)); -+ -+ int count = 0; -+ do -+ { -+ // reached target? -+ if ((_current.getLoc().getGeoX() == _gtx) && (_current.getLoc().getGeoY() == _gty) && (Math.abs(_current.getLoc().getZ() - _gtz) < 8)) -+ { -+ return _current; -+ } -+ -+ // expand current node -+ expand(); -+ -+ // move pointer -+ _current = _current.getChild(); -+ } -+ while ((_current != null) && (count++ < Config.MAX_ITERATIONS)); -+ -+ return null; -+ } -+ -+ public boolean isLocked() -+ { -+ return _lock.tryLock(); -+ } -+ -+ public void free() -+ { -+ _current = null; -+ -+ for (Node[] nodes : _buffer) -+ { -+ for (Node node : nodes) -+ { -+ if (node.getLoc() != null) -+ { -+ node.free(); -+ } -+ } -+ } -+ -+ _lock.unlock(); -+ } -+ -+ /** -+ * Check _current Node and add its neighbors to the buffer. -+ */ -+ private final void expand() -+ { -+ // can't move anywhere, don't expand -+ final byte nswe = _current.getLoc().getNSWE(); -+ if (nswe == 0) -+ { -+ return; -+ } -+ -+ // get geo coords of the node to be expanded -+ final int x = _current.getLoc().getGeoX(); -+ final int y = _current.getLoc().getGeoY(); -+ final short z = (short) _current.getLoc().getZ(); -+ -+ // can move north, expand -+ if ((nswe & GeoStructure.CELL_FLAG_N) != 0) -+ { -+ addNode(x, y - 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move south, expand -+ if ((nswe & GeoStructure.CELL_FLAG_S) != 0) -+ { -+ addNode(x, y + 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_W) != 0) -+ { -+ addNode(x - 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_E) != 0) -+ { -+ addNode(x + 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move north-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NW) != 0) -+ { -+ addNode(x - 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move north-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NE) != 0) -+ { -+ addNode(x + 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SW) != 0) -+ { -+ addNode(x - 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SE) != 0) -+ { -+ addNode(x + 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ } -+ -+ /** -+ * Returns node, if it exists in buffer. -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param z : node Z coord -+ * @return Node : node, if exits in buffer -+ */ -+ private final Node getNode(int x, int y, short z) -+ { -+ // check node X out of coordinates -+ final int ix = x - _cx; -+ if ((ix < 0) || (ix >= _size)) -+ { -+ return null; -+ } -+ -+ // check node Y out of coordinates -+ final int iy = y - _cy; -+ if ((iy < 0) || (iy >= _size)) -+ { -+ return null; -+ } -+ -+ // get node -+ final Node result = _buffer[ix][iy]; -+ -+ // check and update -+ if (result.getLoc() == null) -+ { -+ result.setLoc(x, y, z); -+ } -+ -+ // return node -+ return result; -+ } -+ -+ /** -+ * Add node given by coordinates to the buffer. -+ * @param x : geo X coord -+ * @param y : geo Y coord -+ * @param z : geo Z coord -+ * @param weight : weight of movement to new node -+ */ -+ private final void addNode(int x, int y, short z, int weight) -+ { -+ // get node to be expanded -+ final Node node = getNode(x, y, z); -+ if (node == null) -+ { -+ return; -+ } -+ -+ // Z distance between nearby cells is higher than cell size -+ if (node.getLoc().getZ() > (z + (2 * GeoStructure.CELL_HEIGHT))) -+ { -+ return; -+ } -+ -+ // node was already expanded, return -+ if (node.getCost() >= 0) -+ { -+ return; -+ } -+ -+ node.setParent(_current); -+ if (node.getLoc().getNSWE() != (byte) 0xFF) -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + (weight * Config.OBSTACLE_MULTIPLIER)); -+ } -+ else -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + weight); -+ } -+ -+ Node current = _current; -+ int count = 0; -+ while ((current.getChild() != null) && (count < (Config.MAX_ITERATIONS * 4))) -+ { -+ count++; -+ if (current.getChild().getCost() > node.getCost()) -+ { -+ node.setChild(current.getChild()); -+ break; -+ } -+ current = current.getChild(); -+ } -+ -+ if (count >= (Config.MAX_ITERATIONS * 4)) -+ { -+ System.err.println("Pathfinding: too long loop detected, cost:" + node.getCost()); -+ } -+ -+ current.setChild(node); -+ } -+ -+ /** -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param i : node Z coord -+ * @return double : node cost -+ */ -+ private final double getCostH(int x, int y, int i) -+ { -+ final int dX = x - _gtx; -+ final int dY = y - _gty; -+ final int dZ = (i - _gtz) / GeoStructure.CELL_HEIGHT; -+ -+ // return (Math.abs(dX) + Math.abs(dY) + Math.abs(dZ)) * Config.HEURISTIC_WEIGHT; // Manhattan distance -+ return Math.sqrt((dX * dX) + (dY * dY) + (dZ * dZ)) * Config.HEURISTIC_WEIGHT; // Direct distance -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -- --/** -- * @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 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 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; -- // return super.hashCode(); -- } -- -- @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; -- if (_x != other._x) -- { -- return false; -- } -- if (_y != other._y) -- { -- return false; -- } -- if (_goNorth != other._goNorth) -- { -- return false; -- } -- if (_goEast != other._goEast) -- { -- return false; -- } -- if (_goSouth != other._goSouth) -- { -- return false; -- } -- if (_goWest != other._goWest) -- { -- return false; -- } -- if (_geoHeight != other._geoHeight) -- { -- return false; -- } -- return true; -- } --} -Index: java/org/l2jmobius/gameserver/model/actor/Creature.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/Creature.java (revision 8428) -+++ java/org/l2jmobius/gameserver/model/actor/Creature.java (working copy) -@@ -65,8 +65,6 @@ - import org.l2jmobius.gameserver.enums.TeleportWhereType; - import org.l2jmobius.gameserver.enums.UserInfoType; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; - import org.l2jmobius.gameserver.instancemanager.IdManager; - import org.l2jmobius.gameserver.instancemanager.MapRegionManager; - import org.l2jmobius.gameserver.instancemanager.QuestManager; -@@ -2566,7 +2564,7 @@ - - public boolean disregardingGeodata; - public int onGeodataPathIndex; -- public List geoPath; -+ public List geoPath; - public int geoPathAccurateTx; - public int geoPathAccurateTy; - public int geoPathGtx; -@@ -3443,7 +3441,7 @@ - if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) - { - // Path calculation -- overrides previous movement check -- m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); -+ m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); - if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found - { - if (isPlayer() && !_isFlying && !isInWater) -Index: java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (revision 8424) -+++ java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (working copy) -@@ -12746,7 +12746,7 @@ - { - if (Config.CORRECT_PLAYER_Z) - { -- final int nearestZ = GeoEngine.getInstance().getHigherHeight(getX(), getY(), getZ()); -+ final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); - if (getZ() < nearestZ) - { - teleToLocation(new Location(getX(), getY(), nearestZ)); -Index: java/org/l2jmobius/gameserver/util/GeoUtils.java -=================================================================== ---- java/org/l2jmobius/gameserver/util/GeoUtils.java (revision 8409) -+++ java/org/l2jmobius/gameserver/util/GeoUtils.java (working copy) -@@ -19,7 +19,7 @@ - import java.awt.Color; - - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; - import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; - -@@ -26,25 +26,25 @@ - /** - * @author HorridoJoho - */ --public final class GeoUtils -+public class GeoUtils - { - public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); - - final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); - - while (iter.next()) - { -- final int wx = GeoEngine.getInstance().getWorldX(iter.x()); -- final int wy = GeoEngine.getInstance().getWorldY(iter.y()); -+ final int wx = GeoEngine.getWorldX(iter.x()); -+ final int wy = GeoEngine.getWorldY(iter.y()); - - prim.addPoint(Color.RED, wx, wy, z); - } -@@ -53,21 +53,21 @@ - - public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); -- int wy = GeoEngine.getInstance().getWorldY(prevY); -+ int wx = GeoEngine.getWorldX(prevX); -+ int wy = GeoEngine.getWorldY(prevY); - int wz = iter.z(); - prim.addPoint(Color.RED, wx, wy, wz); - -@@ -78,8 +78,8 @@ - - if ((curX != prevX) || (curY != prevY)) - { -- wx = GeoEngine.getInstance().getWorldX(curX); -- wy = GeoEngine.getInstance().getWorldY(curY); -+ wx = GeoEngine.getWorldX(curX); -+ wy = GeoEngine.getWorldY(curY); - wz = iter.z(); - - prim.addPoint(Color.RED, wx, wy, wz); -@@ -93,7 +93,7 @@ - - private static Color getDirectionColor(int x, int y, int z, int nswe) - { -- if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) -+ if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) - { - return Color.GREEN; - } -@@ -109,9 +109,8 @@ - int iPacket = 0; - - ExServerPrimitive exsp = null; -- final GeoEngine ge = GeoEngine.getInstance(); -- final int playerGx = ge.getGeoX(player.getX()); -- final int playerGy = ge.getGeoY(player.getY()); -+ final int playerGx = GeoEngine.getGeoX(player.getX()); -+ final int playerGy = GeoEngine.getGeoY(player.getY()); - for (int dx = -geoRadius; dx <= geoRadius; ++dx) - { - for (int dy = -geoRadius; dy <= geoRadius; ++dy) -@@ -135,12 +134,12 @@ - final int gx = playerGx + dx; - final int gy = playerGy + dy; - -- final int x = ge.getWorldX(gx); -- final int y = ge.getWorldY(gy); -- final int z = ge.getNearestZ(gx, gy, player.getZ()); -+ final int x = GeoEngine.getWorldX(gx); -+ final int y = GeoEngine.getWorldY(gy); -+ final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); - - // north arrow -- Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); -+ Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); - 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); -@@ -147,7 +146,7 @@ - exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z); - - // east arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); - 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); -@@ -154,13 +153,13 @@ - exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z); - - // south arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); - 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, Cell.NSWE_WEST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); - 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); -@@ -188,15 +187,15 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_EAST; -+ return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_EAST; -+ return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; - } - else - { -- return Cell.NSWE_EAST; -+ return GeoStructure.CELL_FLAG_E; // Direction.EAST; - } - } - else if (x < lastX) // west -@@ -203,26 +202,27 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_WEST; -+ return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_WEST; -+ return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; - } - else - { -- return Cell.NSWE_WEST; -+ return GeoStructure.CELL_FLAG_W; // Direction.WEST; - } - } -- else // unchanged x -+ else -+ // unchanged x - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH; -+ return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH; -+ return GeoStructure.CELL_FLAG_N; // Direction.NORTH; - } - else - { -Index: java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java -=================================================================== ---- java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (nonexistent) -+++ java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (working copy) -@@ -0,0 +1,386 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.tools.geodataconverter; -+ -+import java.io.BufferedOutputStream; -+import java.io.File; -+import java.io.FileOutputStream; -+import java.io.RandomAccessFile; -+import java.nio.ByteOrder; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.Scanner; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.commons.util.PropertiesParser; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoDataConverter -+{ -+ private static GeoFormat _format; -+ private static ABlock[][] _blocks; -+ -+ public static void main(String[] args) -+ { -+ // initialize config -+ loadGeoengineConfigs(); -+ -+ // get geodata format -+ String type = ""; -+ try (Scanner scn = new Scanner(System.in)) -+ { -+ while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) -+ { -+ System.out.println("GeoDataConverter: Select source geodata type:"); -+ System.out.println(" J: L2J (e.g. 23_22.l2j)"); -+ System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); -+ System.out.println(" E: Exit"); -+ System.out.print("Choice: "); -+ type = scn.next(); -+ } -+ } -+ if (type.equalsIgnoreCase("E")) -+ { -+ System.exit(0); -+ } -+ -+ _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; -+ -+ // start conversion -+ System.out.println("GeoDataConverter: Converting all " + _format + " files."); -+ -+ // initialize geodata container -+ _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files -+ int converted = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) -+ { -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) -+ { -+ final String input = String.format(_format.getFilename(), rx, ry); -+ final String filepath = Config.GEODATA_PATH; -+ final File f = new File(filepath + input); -+ if (f.exists() && !f.isDirectory()) -+ { -+ // load geodata -+ if (!loadGeoBlocks(input)) -+ { -+ System.out.println("GeoDataConverter: Unable to load " + input + " region file."); -+ continue; -+ } -+ -+ // recalculate nswe -+ if (!recalculateNswe()) -+ { -+ System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); -+ continue; -+ } -+ -+ // save geodata -+ final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); -+ if (!saveGeoBlocks(output)) -+ { -+ System.out.println("GeoDataConverter: Unable to save " + output + " region file."); -+ continue; -+ } -+ -+ converted++; -+ System.out.println("GeoDataConverter: Created " + output + " region file."); -+ } -+ } -+ } -+ System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); -+ } -+ -+ /** -+ * Loads geo blocks from buffer of the region file. -+ * @param filename : The name of the to load. -+ * @return boolean : True when successful. -+ */ -+ private static boolean loadGeoBlocks(String filename) -+ { -+ // region file is load-able, try to load it -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) -+ { -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) -+ if (_format == GeoFormat.L2OFF) -+ { -+ for (int i = 0; i < 18; i++) -+ { -+ buffer.get(); -+ } -+ } -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ if (_format == GeoFormat.L2J) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2J: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2J: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ else -+ { -+ // get block type -+ final short type = buffer.getShort(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ default: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ if (buffer.remaining() > 0) -+ { -+ System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ return false; -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); -+ return false; -+ } -+ } -+ -+ /** -+ * Recalculate diagonal flags for the region file. -+ * @return boolean : True when successful. -+ */ -+ private static boolean recalculateNswe() -+ { -+ try -+ { -+ for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) -+ { -+ for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) -+ { -+ // get block -+ final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // skip flat blocks -+ if (block instanceof BlockFlat) -+ { -+ continue; -+ } -+ -+ // for complex and multilayer blocks go though all layers -+ short height = Short.MAX_VALUE; -+ int index; -+ while ((index = block.getIndexBelow(x, y, height)) != -1) -+ { -+ // get height and nswe -+ height = block.getHeight(index); -+ byte nswe = block.getNswe(index); -+ -+ // update nswe with diagonal flags -+ nswe = updateNsweBelow(x, y, height, nswe); -+ -+ // set nswe of the cell -+ block.setNswe(index, nswe); -+ } -+ } -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ /** -+ * Updates the NSWE flag with diagonal flags. -+ * @param x : Geodata X coordinate. -+ * @param y : Geodata Y coordinate. -+ * @param z : Geodata Z coordinate. -+ * @param nsweValue : NSWE flag to be updated. -+ * @return byte : Updated NSWE flag. -+ */ -+ private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) -+ { -+ byte nswe = nsweValue; -+ -+ // calculate virtual layer height -+ final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); -+ -+ // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) -+ final byte nsweN = getNsweBelow(x, y - 1, height); -+ final byte nsweS = getNsweBelow(x, y + 1, height); -+ final byte nsweW = getNsweBelow(x - 1, y, height); -+ final byte nsweE = getNsweBelow(x + 1, y, height); -+ -+ // north-west -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NW; -+ } -+ -+ // north-east -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NE; -+ } -+ -+ // south-west -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SW; -+ } -+ -+ // south-east -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SE; -+ } -+ -+ return nswe; -+ } -+ -+ private static byte getNsweBelow(int geoX, int geoY, short worldZ) -+ { -+ // out of geo coordinates -+ if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) -+ { -+ return 0; -+ } -+ -+ // out of geo coordinates -+ if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) -+ { -+ return 0; -+ } -+ -+ // get block -+ final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // get index, when valid, return nswe -+ final int index = block.getIndexBelow(geoX, geoY, worldZ); -+ return index == -1 ? 0 : block.getNswe(index); -+ } -+ -+ /** -+ * Save region file to file. -+ * @param filename : The name of file to save. -+ * @return boolean : True when successful. -+ */ -+ private static boolean saveGeoBlocks(String filename) -+ { -+ try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) -+ { -+ // loop over region blocks and save each block -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[ix][iy].saveBlock(bos); -+ } -+ } -+ -+ // flush data to file -+ bos.flush(); -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ private static void loadGeoengineConfigs() -+ { -+ final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); -+ Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); -+ Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); -+ Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); -+ Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); -+ Config.PATHFINDING = geoData.getBoolean("PathFinding", true); -+ Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -+ Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); -+ Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); -+ Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); -+ Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); -+ Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); -+ } -+} -\ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/geodata/Readme.txt b/L2J_Mobius_3.0_Helios/dist/game/data/geodata/Readme.txt index a480c8c380..2611882e56 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/geodata/Readme.txt +++ b/L2J_Mobius_3.0_Helios/dist/game/data/geodata/Readme.txt @@ -23,8 +23,8 @@ a - Prerequisites b - Make it work ---------------------------------------------- -To make geodata working: -* unpack your geodata files into "/data/geodata" folder -* open "/config/GeoEngine.ini" with your favorite text editor and then edit following config: - - CoordSynchronize = 2 -* If you do not use any geodata files, the server will automatically change this setting to -1. +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. diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java index 037b29d579..89425b0d85 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java +++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java @@ -295,7 +295,7 @@ public class FourSepulchers extends AbstractNpcAI implements IXmlReader { if ((npc != null) && !npc.isDead()) { - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), npc.getSpawn().getLocation().getX() + getRandom(-400, 400), npc.getSpawn().getLocation().getY() + getRandom(-400, 400), npc.getZ(), npc.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), npc.getSpawn().getLocation().getX() + getRandom(-400, 400), npc.getSpawn().getLocation().getY() + getRandom(-400, 400), npc.getZ(), npc.getInstanceWorld()); if (Util.calculateDistance(npc, npc.getSpawn().getLocation(), false, false) < 600) { npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java index 7aa8789df0..00bffa39b8 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java +++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java @@ -270,7 +270,7 @@ public class PrimevalIsle extends AbstractNpcAI final double cos = Math.cos(radian); final int newX = (int) (npc.getX() + (cos * distance)); final int newY = (int) (npc.getY() + (sin * distance)); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, npc.getZ(), npc.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, npc.getZ(), npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, loc, 0); } else if (ag_type == 1) diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java index 0a77f7404d..772f5fb5d7 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java +++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java @@ -78,7 +78,7 @@ public class BoyAndGirl extends AbstractNpcAI startQuestTimer("NPC_SHOUT", 10000 + (getRandom(5) * 1000), npc, null); npc.setRunning(); final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 200, 600); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); return super.onSpawn(npc); } @@ -86,7 +86,7 @@ public class BoyAndGirl extends AbstractNpcAI public void onMoveFinished(Npc npc) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 200, 600); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); super.onMoveFinished(npc); } diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java index 558f2631e6..3bcf3810cb 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java +++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java @@ -48,7 +48,7 @@ public class Devno extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java index 7525cc99e8..ce81f5c05c 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java +++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java @@ -48,7 +48,7 @@ public class Eleve extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java index 7d9150b156..f4e942c326 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java +++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java @@ -46,7 +46,7 @@ public class Handermonkey extends AbstractNpcAI { final int x = npc.getSpawn().getX() + (getRandom(-100, 100)); final int y = npc.getSpawn().getY() + (getRandom(-100, 100)); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x, y, npc.getZ(), npc.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x, y, npc.getZ(), npc.getInstanceWorld()); addMoveToDesire(npc, loc, 0); } else diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java index c993ce8936..7df61da041 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java +++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java @@ -48,7 +48,7 @@ public class Karonf extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java index 6e8cbee920..589d376f6b 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java +++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java @@ -48,7 +48,7 @@ public class Marsha extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java index c616abccc6..5c09d531c8 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java +++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java @@ -48,7 +48,7 @@ public class Morgan extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java index 3652b4ab02..fe20952121 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java +++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java @@ -48,7 +48,7 @@ public class Rubentis extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", 10000 + (getRandom(5) * 1000), npc, null); } diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java index beece64521..b0944f763d 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java +++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java @@ -63,7 +63,7 @@ public class Vortex extends AbstractNpcAI final int x = (int) (attackers.getX() + (600 * Math.cos(radians))); final int y = (int) (attackers.getY() + (600 * Math.sin(radians))); final int z = attackers.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(attackers.getX(), attackers.getY(), attackers.getZ(), x, y, z, attackers.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(attackers.getX(), attackers.getY(), attackers.getZ(), x, y, z, attackers.getInstanceWorld()); attackers.broadcastPacket(new FlyToLocation(attackers, x, y, z, FlyToLocation.FlyType.THROW_UP, 800, 800, 800)); attackers.setXYZ(loc); attackers.broadcastPacket(new ValidateLocation(attackers)); diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/others/FleeMonsters.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/others/FleeMonsters.java index 9346aebc95..b5de804b28 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/others/FleeMonsters.java +++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/others/FleeMonsters.java @@ -68,7 +68,7 @@ public class FleeMonsters extends AbstractNpcAI final int posX = (int) (npc.getX() + (FLEE_DISTANCE * Math.cos(radians))); final int posY = (int) (npc.getY() + (FLEE_DISTANCE * Math.sin(radians))); final int posZ = npc.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, attacker.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); return super.onAttack(npc, attacker, damage, isSummon); } diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index 524652cbc1..18511c6541 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { @@ -133,8 +133,8 @@ public class AdminGeodata implements IAdminCommandHandler } case "admin_geomap": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y + " (" + ((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + "," + ((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + " to " + ((((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + "," + ((((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + ")"); break; } diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java index f3dffa1227..76572f7f25 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java +++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java @@ -57,8 +57,8 @@ public class AdminMissingHtmls implements IAdminCommandHandler { case "admin_geomap_missing_htmls": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final int topLeftX = (x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE; final int topLeftY = (y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE; final int bottomRightX = (((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1; diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index fdb2319138..a0a34f1051 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -19,9 +19,9 @@ package handlers.admincommandhandlers; import java.util.List; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; +import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.util.BuilderUtil; @@ -44,13 +44,13 @@ public class AdminPathNode implements IAdminCommandHandler } if (activeChar.getTarget() != null) { - final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { BuilderUtil.sendSysMessage(activeChar, "No Route!"); return true; } - for (AbstractNodeLoc a : path) + for (Location a : path) { BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/Blink.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/Blink.java index 22f2c84eae..22697e8e20 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/Blink.java +++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/Blink.java @@ -88,7 +88,7 @@ public class Blink extends AbstractEffect final int y = effected.getY() + y1; final int z = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, destination, _flyType, _flySpeed, _flyDelay, _animationSpeed)); diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/Fear.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/Fear.java index 95a5afdf98..88c795e04b 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/Fear.java +++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/Fear.java @@ -100,7 +100,7 @@ public class Fear extends AbstractEffect final int posY = (int) (effected.getY() + (FEAR_RANGE * Math.sin(radians))); final int posZ = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); } } diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java index fddd94c60a..55c2e78d8a 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java +++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java @@ -57,7 +57,7 @@ public class FlyAway extends AbstractEffect final int y = (int) (effector.getY() - (nRadius * (dy / distance))); final int z = effector.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.broadcastPacket(new FlyToLocation(effected, destination, FlyType.THROW_UP)); effected.setXYZ(destination); diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java index 3c3f9aca62..14809ac499 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java +++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java @@ -179,7 +179,7 @@ public class KnockBack extends AbstractEffect final int x = (int) (effected.getX() + (_distance * Math.cos(radians))); final int y = (int) (effected.getY() + (_distance * Math.sin(radians))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, loc, _type, _speed, _delay, _animationSpeed)); diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/PullBack.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/PullBack.java index ed6a37eff6..38fe2fe9d0 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/PullBack.java +++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/PullBack.java @@ -73,7 +73,7 @@ public class PullBack extends AbstractEffect } // In retail, you get debuff, but you are not even moved if there is obstacle. You are still disabled from using skills and moving though. - if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effector.getInstanceWorld())) + if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effected.getInstanceWorld())) { effected.broadcastPacket(new FlyToLocation(effected, effector, _type, _speed, _delay, _animationSpeed)); effected.setXYZ(effector.getX(), effector.getY(), GeoEngine.getInstance().getHeight(effector.getX(), effector.getY(), effector.getZ()) + 10); diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java index d6f9664141..813e496c47 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java +++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java @@ -87,7 +87,7 @@ public class TeleportToSummon extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = summon.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java index d3c263978c..c9dc34ac22 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java +++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java @@ -76,7 +76,7 @@ public class TeleportToTarget extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java index f2148adb54..c7ca6f64cb 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java +++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java @@ -65,7 +65,7 @@ public class RollingDice implements IItemHandler final int x = player.getX() + x1; final int y = player.getY() + y1; final int z = player.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); Broadcast.toSelfAndKnownPlayers(player, new Dice(player.getObjectId(), itemId, number, destination.getX(), destination.getY(), destination.getZ())); final SystemMessage sm = new SystemMessage(SystemMessageId.C1_HAS_ROLLED_A_S2); diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/targethandlers/Ground.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/targethandlers/Ground.java index 328c9fb38a..2d4b94e385 100644 --- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/targethandlers/Ground.java +++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/handlers/targethandlers/Ground.java @@ -52,7 +52,7 @@ public class Ground implements ITargetTypeHandler return null; } - if (!GeoEngine.getInstance().canSeeTarget(creature, worldPosition)) + if (!GeoEngine.getInstance().canSeeLocation(creature, worldPosition)) { if (sendMessage) { diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/Config.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/Config.java index 18eb5bd9a3..e107c72ac1 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/Config.java @@ -61,6 +61,7 @@ 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; @@ -955,12 +956,17 @@ public class Config // GeoEngine // -------------------------------------------------- public static Path GEODATA_PATH; + public static GeoType GEODATA_TYPE; public static boolean PATHFINDING; public static String PATHFIND_BUFFERS; - public static float LOW_WEIGHT; - public static float MEDIUM_WEIGHT; - public static float HIGH_WEIGHT; - public static float DIAGONAL_WEIGHT; + public static int MOVE_WEIGHT; + public static int MOVE_WEIGHT_DIAG; + public static int OBSTACLE_WEIGHT; + 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; /** Attribute System */ public static int S_WEAPON_STONE; @@ -2513,12 +2519,17 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); + GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); - MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); - HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); - DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); + MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); + OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); + HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); + MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); + MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); // Load AllowedPlayerRaces config file (if exists) final PropertiesParser AllowedPlayerRaces = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE); diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/commons/util/CommonUtil.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/commons/util/CommonUtil.java index 14ae3a4560..ab7837d827 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/commons/util/CommonUtil.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/commons/util/CommonUtil.java @@ -584,4 +584,15 @@ public class CommonUtil final DecimalFormat formatter = new DecimalFormat(format, new DecimalFormatSymbols(Locale.ENGLISH)); return formatter.format(value); } + + /** + * @param numToTest : The number to test. + * @param min : The minimum limit. + * @param max : The maximum limit. + * @return the number or one of the limit (mininum / maximum). + */ + public static int limit(int numToTest, int min, int max) + { + return (numToTest > max) ? max : ((numToTest < min) ? min : numToTest); + } } diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/commons/util/Point2D.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/commons/util/Point2D.java new file mode 100644 index 0000000000..ebb6272e5e --- /dev/null +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/commons/util/Point2D.java @@ -0,0 +1,180 @@ +/* + * 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 . + */ +package org.l2jmobius.commons.util; + +import java.util.Objects; + +/** + * A datatype used to retain a 2D (x/y) point. It got the capability to be set and cleaned. + */ +public class Point2D +{ + protected volatile int _x; + protected volatile int _y; + + public Point2D(int x, int y) + { + _x = x; + _y = y; + } + + @Override + public Point2D clone() + { + return new Point2D(_x, _y); + } + + @Override + public String toString() + { + return _x + ", " + _y; + } + + @Override + public int hashCode() + { + return Objects.hash(_x, _y); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final Point2D other = (Point2D) obj; + return (_x == other._x) && (_y == other._y); + } + + /** + * @param x : The X coord to test. + * @param y : The Y coord to test. + * @return True if all coordinates equals this {@link Point2D} coordinates. + */ + public boolean equals(int x, int y) + { + return (_x == x) && (_y == y); + } + + public int getX() + { + return _x; + } + + public void setX(int x) + { + _x = x; + } + + public int getY() + { + return _y; + } + + public void setY(int y) + { + _y = y; + } + + public void set(int x, int y) + { + _x = x; + _y = y; + } + + /** + * Refresh the current {@link Point2D} using a reference {@link Point2D} and a distance. The new destination is calculated to go in opposite side of the {@link Point2D} reference.
+ *
+ * This method is perfect to calculate fleeing characters position. + * @param referenceLoc : The Point2D used as position. + * @param distance : The distance to be set between current and new position. + */ + public void setFleeing(Point2D referenceLoc, int distance) + { + final double xDiff = referenceLoc.getX() - _x; + final double yDiff = referenceLoc.getY() - _y; + + final double yxRation = Math.abs(xDiff / yDiff); + + final int y = (int) (distance / (yxRation + 1)); + final int x = (int) (y * yxRation); + + _x += (xDiff < 0 ? x : -x); + _y += (yDiff < 0 ? y : -y); + } + + public void clean() + { + _x = 0; + _y = 0; + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @return The distance between this {@Point2D} and some given coordinates. + */ + public double distance2D(int x, int y) + { + final double dx = (double) _x - x; + final double dy = (double) _y - y; + + return Math.sqrt((dx * dx) + (dy * dy)); + } + + /** + * @param point : The {@link Point2D} to test. + * @return The distance between this {@Point2D} and the {@link Point2D} set as parameter. + */ + public double distance2D(Point2D point) + { + return distance2D(point.getX(), point.getY()); + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of some given coordinates. + */ + public boolean isIn2DRadius(int x, int y, int radius) + { + return distance2D(x, y) < radius; + } + + /** + * @param point : The Point2D to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of the {@link Point2D} set as parameter. + */ + public boolean isIn2DRadius(Point2D point, int radius) + { + return distance2D(point) < radius; + } +} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/AttackableAI.java index 547963e3e8..dc29d4cf6e 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/AttackableAI.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/ai/AttackableAI.java @@ -578,7 +578,7 @@ public class AttackableAI extends CreatureAI } // Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast) - final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); + final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); moveTo(moveLoc.getX(), moveLoc.getY(), moveLoc.getZ()); } } @@ -801,7 +801,7 @@ public class AttackableAI extends CreatureAI final int newZ = npc.getZ() + 30; // Mobius: Verify destination. Prevents wall collision issues and fixes monsters not avoiding obstacles. - moveTo(GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); + moveTo(GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); } return; } diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/data/SpawnTable.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/data/SpawnTable.java index a81201f69e..19f0db5b3e 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/data/SpawnTable.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/data/SpawnTable.java @@ -114,8 +114,8 @@ public class SpawnTable } // XML file for spawn - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); // Write info to XML @@ -192,8 +192,8 @@ public class SpawnTable if (update) { - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = spawn.getNpcSpawnTemplate() != null ? spawn.getNpcSpawnTemplate().getSpawnTemplate().getFile() : new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); final File tempFile = new File(spawnFile.getAbsolutePath().substring(Config.DATAPACK_ROOT.getAbsolutePath().length() + 1).replace('\\', '/') + ".tmp"); try diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/enums/GeoType.java similarity index 69% rename from L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java rename to L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/enums/GeoType.java index 7223b5b2be..f77aa5eac4 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/enums/GeoType.java @@ -14,20 +14,22 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.l2jmobius.gameserver.geoengine.pathfinding; +package org.l2jmobius.gameserver.enums; -/** - * @author -Nemesiss- - */ -public abstract class AbstractNodeLoc +public enum GeoType { - public abstract int getX(); + L2J("%d_%d.l2j"), + L2OFF("%d_%d_conv.dat"); - public abstract int getY(); + private final String _filename; - public abstract int getZ(); + private GeoType(String filename) + { + _filename = filename; + } - public abstract int getNodeX(); - - public abstract int getNodeY(); -} + public String getFilename() + { + return _filename; + } +} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java new file mode 100644 index 0000000000..e62f50a8df --- /dev/null +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java @@ -0,0 +1,144 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 9d255a5f54..7bef9c770b 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,72 +16,63 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.channels.FileChannel.MapMode; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.logging.Level; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; import java.util.logging.Logger; import org.l2jmobius.Config; +import org.l2jmobius.commons.util.CommonUtil; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; -import org.l2jmobius.gameserver.geoengine.geodata.IRegion; -import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; -import org.l2jmobius.gameserver.geoengine.geodata.Region; +import org.l2jmobius.gameserver.enums.GeoType; +import org.l2jmobius.gameserver.enums.MoveDirectionType; +import org.l2jmobius.gameserver.geoengine.geodata.ABlock; +import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; +import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; +import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; +import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.model.interfaces.ILocational; -import org.l2jmobius.gameserver.util.GeoUtils; -import org.l2jmobius.gameserver.util.LinePointIterator; -import org.l2jmobius.gameserver.util.LinePointIterator3D; -/** - * @author -Nemesiss-, HorridoJoho - */ public class GeoEngine { - private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - - private static final int WORLD_MIN_X = -655360; - private static final int WORLD_MIN_Y = -589824; - private static final int WORLD_MIN_Z = -16384; - - /** 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; - - /** The regions array */ - private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; - private static final int ELEVATED_SEE_OVER_DISTANCE = 2; - private static final int MAX_SEE_OVER_HEIGHT = 48; - protected GeoEngine() + private final ABlock[][] _blocks; + private final BlockNull _nullBlock; + + // Pre-allocated buffers. + private final BufferHolder[] _buffers; + + public GeoEngine() { LOGGER.info("GeoEngine: Initializing..."); - for (int i = 0; i < _regions.length(); i++) - { - _regions.set(i, NullRegion.INSTANCE); - } + // Initialize block container. + _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; + + // Load null block. + _nullBlock = new BlockNull(); + + // Initialize multilayer temporarily buffer. + BlockMultilayer.initialize(); + + // Load geo files according to geoengine config setup. int loaded = 0; try { @@ -92,23 +83,52 @@ public class GeoEngine final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); if (Files.exists(geoFilePath)) { - try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + // Region file is load-able, try to load it. + if (loadGeoBlocks(regionX, regionY)) { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); loaded++; } } + else + { + // Region file is not load-able, load null blocks. + loadNullBlocks(regionX, regionY); + } } } } catch (Exception e) { - LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + LOGGER.warning("GeoEngine: Failed to load geodata! " + e); System.exit(1); } - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + // Release multilayer block temporarily buffer. + BlockMultilayer.release(); + + String[] array = Config.PATHFIND_BUFFERS.split(";"); + _buffers = new BufferHolder[array.length]; + + int count = 0; + for (int i = 0; i < array.length; i++) + { + String buf = array[i]; + String[] args = buf.split("x"); + + try + { + int size = Integer.parseInt(args[1]); + count += size; + _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); + } + catch (Exception e) + { + LOGGER.warning("Could not load buffer setting:" + buf + ". " + e); + } + } + LOGGER.info("Loaded " + count + " node buffers."); + // Avoid wrong configs when no files are loaded. if ((loaded == 0) && Config.PATHFINDING) { @@ -118,616 +138,913 @@ public class GeoEngine } /** - * @param geoX - * @param geoY - * @return the region + * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer and log this situation. + * @param size : pre-calculated minimal required size + * @return NodeBuffer : buffer */ - private IRegion getRegion(int geoX, int geoY) + private NodeBuffer getBuffer(int size) { - final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); - if ((region < 0) || (region >= _regions.length())) + NodeBuffer current = null; + for (BufferHolder holder : _buffers) { - return null; - } - return _regions.get(region); - } - - /** - * @param filePath - * @param regionX - * @param regionY - * @throws IOException - */ - 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()).load().order(ByteOrder.LITTLE_ENDIAN))); - } - } - - /** - * @param regionX - * @param regionY - */ - public void unloadRegion(int regionX, int regionY) - { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); - } - - /** - * @param geoX - * @param geoY - * @return if geodata exist - */ - public boolean hasGeoPos(int geoX, int geoY) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return false; - } - return region.hasGeo(); - } - - /** - * Checks the specified position for available geodata. - * @param x the world x - * @param y the world y - * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise - */ - public boolean hasGeo(int x, int y) - { - return hasGeoPos(getGeoX(x), getGeoY(y)); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe check - */ - public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return true; - } - return region.checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe anti-corner cut - */ - public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) - { - boolean can = true; - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - return can && checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the nearest Z value - */ - public int getNearestZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNearestZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next lower Z value - */ - public int getNextLowerZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextLowerZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next higher Z value - */ - public int getNextHigherZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextHigherZ(geoX, geoY, worldZ); - } - - /** - * Gets the Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHeight(int x, int y, int z) - { - return getNearestZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next lower Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getLowerHeight(int x, int y, int z) - { - return getNextLowerZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next higher Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHigherHeight(int x, int y, int z) - { - return getNextHigherZ(getGeoX(x), getGeoY(y), z); - } - - /** - * @param worldX - * @return the geo X - */ - public int getGeoX(int worldX) - { - return (worldX - WORLD_MIN_X) / 16; - } - - /** - * @param worldY - * @return the geo Y - */ - public int getGeoY(int worldY) - { - return (worldY - WORLD_MIN_Y) / 16; - } - - /** - * @param worldZ - * @return the geo Z - */ - public int getGeoZ(int worldZ) - { - return (worldZ - WORLD_MIN_Z) / 16; - } - - /** - * @param geoX - * @return the world X - */ - public int getWorldX(int geoX) - { - return (geoX * 16) + WORLD_MIN_X + 8; - } - - /** - * @param geoY - * @return the world Y - */ - public int getWorldY(int geoY) - { - return (geoY * 16) + WORLD_MIN_Y + 8; - } - - /** - * @param geoZ - * @return the world Z - */ - public int getWorldZ(int geoZ) - { - return (geoZ * 16) + WORLD_MIN_Z + 8; - } - - /** - * Can see target. Doors as target always return true. Checks doors between. - * @param cha the character - * @param target the target - * @return {@code true} if the character can see the target (LOS), {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, WorldObject target) - { - if (target.isDoor()) - { - // Can always see doors. - return true; - } - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); - } - - /** - * Can see target. Checks doors between. - * @param cha the character - * @param worldPosition the world position - * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) - { - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param world - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @param tworld the target's instanceId - * @return - */ - public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) - { - return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instance - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) - { - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) - { - return false; - } - return canSeeTarget(x, y, z, tx, ty, tz); - } - - /** - * @param prevX - * @param prevY - * @param prevGeoZ - * @param curX - * @param curY - * @param nswe - * @return the LOS Z value - */ - private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) - { - if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) - { - throw new RuntimeException("Multiple directions!"); - } - - if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) - { - return getNearestZ(curX, curY, prevGeoZ); - } - - return getNextHigherZ(curX, curY, prevGeoZ); - } - - /** - * Can see target. Does not check doors between. - * @param xValue the x coordinate - * @param yValue the y coordinate - * @param zValue the z coordinate - * @param txValue the target's x coordinate - * @param tyValue the target's y coordinate - * @param tzValue the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) - { - int x = xValue; - int y = yValue; - int tx = txValue; - int ty = tyValue; - - int geoX = getGeoX(x); - int geoY = getGeoY(y); - int tGeoX = getGeoX(tx); - int tGeoY = getGeoY(ty); - - int z = getNearestZ(geoX, geoY, zValue); - int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - // Fastpath. - if ((geoX == tGeoX) && (geoY == tGeoY)) - { - if (hasGeoPos(tGeoX, tGeoY)) - { - return z == tz; - } - return true; - } - - if (tz > z) - { - int tmp = tx; - tx = x; - x = tmp; - - tmp = ty; - ty = y; - y = tmp; - - tmp = tz; - tz = z; - z = tmp; - - tmp = tGeoX; - tGeoX = geoX; - geoX = tmp; - - tmp = tGeoY; - tGeoY = geoY; - geoY = tmp; - } - - final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); - // First point is guaranteed to be available, skip it, we can always see our own position. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - final int prevZ = pointIter.z(); - int prevGeoZ = prevZ; - int ptIndex = 0; - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - - if ((curX == prevX) && (curY == prevY)) + // Find proper size of buffer. + if (holder._size < size) { continue; } - final int beeCurZ = pointIter.z(); - int curGeoZ = prevGeoZ; - - // Check if the position has geodata. - if (hasGeoPos(curX, curY)) + // Find unlocked NodeBuffer. + for (NodeBuffer buffer : holder._buffer) { - final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); - curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); - int maxHeight; - if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) + if (!buffer.isLocked()) { - maxHeight = z + MAX_SEE_OVER_HEIGHT; - } - else - { - maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; + continue; } - boolean canSeeThrough = false; - if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) + return buffer; + } + + LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); + + // NodeBuffer not found, allocate temporary buffer. + current = new NodeBuffer(holder._size); + current.isLocked(); + } + + return current; + } + + /** + * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + * @return boolean : True, when geodata file was loaded without problem. + */ + private boolean loadGeoBlocks(int regionX, int regionY) + { + final String filename = String.format(Config.GEODATA_TYPE.getFilename(), regionX, regionY); + final String filepath = Config.GEODATA_PATH + "\\" + filename; + + // Standard load. + try (RandomAccessFile raf = new RandomAccessFile(filepath, "r"); + FileChannel fc = raf.getChannel()) + { + // Initialize file buffer. + MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // Load 18B header for L2OFF geodata (1st and 2nd byte...region X and Y). + if (Config.GEODATA_TYPE == GeoType.L2OFF) + { + for (int i = 0; i < 18; i++) { - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) + buffer.get(); + } + } + + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Loop over region blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + if (Config.GEODATA_TYPE == GeoType.L2J) { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) - { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); + // Get block type. + final byte type = buffer.get(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + case GeoStructure.TYPE_MULTILAYER_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + default: + { + throw new IllegalArgumentException("Unknown block type: " + type); + } + } } else { - canSeeThrough = true; + // Get block type. + final short type = buffer.getShort(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + default: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + } } } + } + + // Check data consistency. + if (buffer.remaining() > 0) + { + LOGGER.warning("Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); + } + + // Loading was successful. + return true; + } + catch (Exception e) + { + // An error occured while loading, load null blocks. + LOGGER.warning("Error loading " + filename + " region file. " + e); + + // Replace whole region file with null blocks. + loadNullBlocks(regionX, regionY); + + // Loading was not successful. + return false; + } + } + + /** + * Loads null blocks. Used when no region file is detected or an error occurs during loading. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + */ + private void loadNullBlocks(int regionX, int regionY) + { + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Load all null blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + _blocks[blockX + ix][blockY + iy] = _nullBlock; + } + } + } + + /** + * Converts world X to geodata X. + * @param worldX + * @return int : Geo X + */ + public static int getGeoX(int worldX) + { + return (worldX - World.WORLD_X_MIN) >> 4; + } + + /** + * Converts world Y to geodata Y. + * @param worldY + * @return int : Geo Y + */ + public static int getGeoY(int worldY) + { + return (worldY - World.WORLD_Y_MIN) >> 4; + } + + /** + * Converts geodata X to world X. + * @param geoX + * @return int : World X + */ + public static int getWorldX(int geoX) + { + return (geoX << 4) + World.WORLD_X_MIN + 8; + } + + /** + * Converts geodata Y to world Y. + * @param geoY + * @return int : World Y + */ + public static int getWorldY(int geoY) + { + return (geoY << 4) + World.WORLD_Y_MIN + 8; + } + + /** + * Returns block of geodata on given coordinates. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return {@link ABlock} : Block of geodata. + */ + public ABlock getBlock(int geoX, int geoY) + { + final int x = geoX / GeoStructure.BLOCK_CELLS_X; + if ((x < 0) || (x >= GeoStructure.GEO_BLOCKS_X)) + { + return null; + } + final int y = geoY / GeoStructure.BLOCK_CELLS_Y; + if ((y < 0) || (y >= GeoStructure.GEO_BLOCKS_Y)) + { + return null; + } + return _blocks[x][y]; + } + + /** + * Check if geo coordinates has geo. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return boolean : True, if given geo coordinates have geodata + */ + public boolean hasGeoPos(int geoX, int geoY) + { + final ABlock block = getBlock(geoX, geoY); + return (block != null) && block.hasGeoPos(); + } + + /** + * Returns the height of cell, which is closest to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell geodata Z coordinate, closest to given coordinates. + */ + public short getHeightNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return (short) worldZ; + } + return block.getHeightNearest(geoX, geoY, worldZ); + } + + /** + * Returns the NSWE flag byte of cell, which is closes to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. + */ + public byte getNsweNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return GeoStructure.CELL_FLAG_ALL; + } + return block.getNsweNearest(geoX, geoY, worldZ); + } + + /** + * Check if world coordinates has geo. + * @param worldX : World X + * @param worldY : World Y + * @return boolean : True, if given world coordinates have geodata + */ + public boolean hasGeo(int worldX, int worldY) + { + return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param loc : The location used as reference. + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(Location loc) + { + return getHeightNearest(getGeoX(loc.getX()), getGeoY(loc.getY()), loc.getZ()); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param worldX : world x + * @param worldY : world y + * @param worldZ : world z + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(int worldX, int worldY, int worldZ) + { + return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); + } + + /** + * Check line of sight from {@link WorldObject} to {@link WorldObject}.
+ * @param object : The origin object. + * @param target : The target object. + * @return True, when object can see target. + */ + public boolean canSeeTarget(WorldObject object, WorldObject target) + { + // Can always see doors. + if (target.isDoor()) + { + return true; + } + + if (object.getInstanceWorld() != target.getInstanceWorld()) + { + return false; + } + + if (DoorData.getInstance().checkIfDoorsBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld())) + { + return false; + } + + // Get object's and target's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + double theight = 0; + if (target instanceof Creature) + { + theight += (((Creature) target).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + return canSee(object.getX(), object.getY(), object.getZ(), oheight, target.getX(), target.getY(), target.getZ(), theight) && canSee(target.getX(), target.getY(), target.getZ(), theight, object.getX(), object.getY(), object.getZ(), oheight); + } + + /** + * Check line of sight from {@link WorldObject} to {@link Location}.
+ * Note: The check uses {@link Location}'s real Z coordinate (e.g. point above ground), not its geodata representation. + * @param object : The origin object. + * @param position : The target position. + * @return True, when object can see position. + */ + public boolean canSeeLocation(WorldObject object, Location position) + { + // Get object and location coordinates. + int ox = object.getX(); + int oy = object.getY(); + int oz = object.getZ(); + int tx = position.getX(); + int ty = position.getY(); + int tz = position.getZ(); + + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld())) + { + return false; + } + + // Get object's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + // Perform geodata check. + return canSee(ox, oy, oz, oheight, tx, ty, tz, 0) && canSee(tx, ty, tz, 0, ox, oy, oz, oheight); + } + + /** + * Simple check for origin to target visibility.
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param oheight : The height of origin, used as start point. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param theight : The height of target, used as end point. + * @return True, when origin can see target. + */ + public boolean canSee(int ox, int oy, int oz, double oheight, int tx, int ty, int tz, double theight) + { + // Get origin geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get target geodata coordinates. + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check being on same cell and layer (index). + // Note: Get index must use origin height increased by cell height, the method returns index to height exclusive self. + int index = block.getIndexBelow(gox, goy, oz + GeoStructure.CELL_HEIGHT); + if (index < 0) + { + return false; + } + + if ((gox == gtx) && (goy == gty)) + { + return index == block.getIndexBelow(gtx, gty, tz + GeoStructure.CELL_HEIGHT); + } + + // Get ground and nswe flag. + int groundZ = block.getHeight(index); + int nswe = block.getNswe(index); + + // Get delta coordinates, slope of line (XY, XZ) and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double dz = (tz + theight) - (oz + oheight); + final double m = (double) dy / dx; + final double mz = dz / Math.sqrt((dx * dx) + (dy * dy)); + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); + + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) + { + // Set next cell in X direction. + gridX += mdt.getStepX(); + gox += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); - if (!canSeeThrough) - { - return false; - } + // Set next cell in Y direction. + gridY += mdt.getStepY(); + goy += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevGeoZ = curGeoZ; - ++ptIndex; + // Get block of the next cell. + block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get line of sight height (including Z slope). + double losz = oz + oheight + Config.MAX_OBSTACLE_HEIGHT; + losz += mz * Math.sqrt(((checkX - ox) * (checkX - ox)) + ((checkY - oy) * (checkY - oy))); + + // Check line of sight going though wall (vertical check). + + // Get index of particular layer, based on last iterated cell conditions. + boolean canMove = (nswe & dir) != 0; + if (canMove) + { + // No wall present, get next cell below current cell. + index = block.getIndexBelow(gox, goy, groundZ + GeoStructure.CELL_IGNORE_HEIGHT); + } + else + { + // Wall present, get next cell above current cell. + index = block.getIndexAbove(gox, goy, groundZ - (2 * GeoStructure.CELL_HEIGHT)); + } + + // Next cell's does not exist (no geodata with valid condition), return fail. + if (index < 0) + { + return false; + } + + // Get next cell's layer height. + int z = block.getHeight(index); + + // Perform sine of sight check (next cell is above line of sight line), return fail. + if (z > losz) + { + return false; + } + + // Next cell is accessible, update z and NSWE. + groundZ = z; + nswe = block.getNswe(index); } + // Iteration is completed, no obstacle is found. return true; } /** - * Move check. - * @param x the x coordinate - * @param y the y coordinate - * @param zValue the z coordinate - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tzValue the target's z coordinate - * @param instance the instance - * @return the last Location (x,y,z) where player can walk - just before wall + * Check movement from coordinates to coordinates.
+ * Note: The Z coordinates are supposed to be already validated geodata coordinates. + * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return True, when target coordinates are reachable from origin coordinates. */ - public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(x); - final int geoY = getGeoY(y); - final int z = getNearestZ(geoX, geoY, zValue); - final int tGeoX = getGeoX(tx); - final int tGeoY = getGeoY(ty); - final int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return new Location(x, y, getHeight(x, y, z)); - } - if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) - { - return new Location(x, y, getHeight(x, y, z)); + return false; } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = z; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return false; + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + int goz = getHeightNearest(gox, goy, oz); + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check movement within same cell. + if ((gox == gtx) && (goy == gty)) + { + return goz == getHeight(tx, ty, tz); + } + + // Get nswe flag. + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - // Can't move, return previous location. - return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check point heading into obstacle, if so return current point. + if ((nswe & dir) == 0) + { + return false; + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return false; + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != tz)) - { - // Different floors, return start location. - return new Location(x, y, z); - } - - return new Location(tx, ty, tz); + // When origin Z is target Z, the move is successful. + return goz == getHeight(tx, ty, tz); } /** - * Checks if its possible to move from one location to another. - * @param fromX the X coordinate to start checking from - * @param fromY the Y coordinate to start checking from - * @param fromZvalue the Z coordinate to start checking from - * @param toX the X coordinate to end checking at - * @param toY the Y coordinate to end checking at - * @param toZvalue the Z coordinate to end checking at - * @param instance the instance - * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + * Check movement from origin to target coordinates. Returns last available point in the checked path.
+ * Target X and Y reachable and Z is on same floor: + *
    + *
  • Location of the target with corrected Z value from geodata.
  • + *
+ * Target X and Y reachable but Z is on another floor: + *
    + *
  • Location of the origin with corrected Z value from geodata.
  • + *
+ * Target X and Y not reachable: + *
    + *
  • Last accessible location in destination to target.
  • + *
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return The {@link Location} representing last point of movement (e.g. just before wall). */ - public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + public Location getValidLocation(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(fromX); - final int geoY = getGeoY(fromY); - final int fromZ = getNearestZ(geoX, geoY, fromZvalue); - final int tGeoX = getGeoX(toX); - final int tGeoY = getGeoY(toY); - final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); - - if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) - { - return false; + return new Location(ox, oy, oz); } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = fromZ; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return new Location(ox, oy, oz); + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + final int gtz = getHeightNearest(gtx, gty, tz); + int goz = getHeightNearest(gox, goy, oz); + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - return false; - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check target cell is out of geodata grid (world coordinates). + if ((nx < 0) || (nx >= GeoStructure.GEO_CELLS_X) || (ny < 0) || (ny >= GeoStructure.GEO_CELLS_Y)) + { + return new Location(checkX, checkY, goz); + } + + // Check point heading into obstacle, if so return current (border) point. + if ((nswe & dir) == 0) + { + return new Location(checkX, checkY, goz); + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current (border) point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return new Location(checkX, checkY, goz); + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) - { - // Different floors. - return false; - } - - return true; + // Compare Z coordinates: + // If same, path is okay, return target point and fix its Z geodata coordinate. + // If not same, path is does not exist, return origin point. + return goz == gtz ? new Location(tx, ty, gtz) : new Location(ox, oy, oz); } + /** + * Returns the list of location objects as a result of complete path calculation. + * @param ox : origin x + * @param oy : origin y + * @param oz : origin z + * @param tx : target x + * @param ty : target y + * @param tz : target z + * @param instance + * @return {@code List} : complete path from nodes + */ + public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + { + // Get origin and check existing geo coords. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + if (!hasGeoPos(gox, goy)) + { + return Collections.emptyList(); + } + + int goz = getHeightNearest(gox, goy, oz); + + // Get target and check existing geo coords. + int gtx = getGeoX(tx); + int gty = getGeoY(ty); + if (!hasGeoPos(gtx, gty)) + { + return Collections.emptyList(); + } + + int gtz = getHeightNearest(gtx, gty, tz); + + // Prepare buffer for pathfinding calculations. + NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + if (buffer == null) + { + return Collections.emptyList(); + } + + // Find path. + List path = null; + try + { + path = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + if (path.isEmpty()) + { + return Collections.emptyList(); + } + } + catch (Exception e) + { + return Collections.emptyList(); + } + finally + { + buffer.free(); + } + + // Check path. + if (path.size() < 3) + { + return path; + } + + // Get path list iterator. + ListIterator point = path.listIterator(); + + // Get node A (origin). + int nodeAx = ox; + int nodeAy = oy; + int nodeAz = goz; + + // Get node B. + Location nodeB = point.next(); + + // Iterate thought the path to optimize it. + while (point.hasNext()) + { + // Get node C. + Location nodeC = path.get(point.nextIndex()); + + // Check movement from node A to node C. + if (canMoveToTarget(nodeAx, nodeAy, nodeAz, nodeC.getX(), nodeC.getY(), nodeC.getZ(), instance)) + { + // Can move from node A to node C. + // Remove node B. + point.remove(); + } + else + { + // Can not move from node A to node C. + // Set node A (node B is part of path, update A coordinates). + nodeAx = nodeB.getX(); + nodeAy = nodeB.getY(); + nodeAz = nodeB.getZ(); + } + + // Set node B. + nodeB = point.next(); + } + + return path; + } + + /** + * NodeBuffer container with specified size and count of separate buffers. + */ + private static class BufferHolder + { + final int _size; + final List _buffer; + + public BufferHolder(int size, int count) + { + _size = size; + _buffer = new ArrayList<>(count); + + for (int i = 0; i < count; i++) + { + _buffer.add(new NodeBuffer(size)); + } + } + } + + /** + * Returns the instance of the {@link GeoEngine}. + * @return {@link GeoEngine} : The instance. + */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -737,4 +1054,4 @@ public class GeoEngine { protected static final GeoEngine INSTANCE = new GeoEngine(); } -} +} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java deleted file mode 100644 index cc76b7523a..0000000000 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ /dev/null @@ -1,301 +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 . - */ -package org.l2jmobius.gameserver.geoengine; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; -import org.l2jmobius.gameserver.model.World; -import org.l2jmobius.gameserver.model.instancezone.Instance; - -/** - * @author -Nemesiss- - */ -public class GeoEnginePathfinding -{ - private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); - - private BufferInfo[] _buffers; - - protected GeoEnginePathfinding() - { - try - { - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - - _buffers = 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); - } - - _buffers[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"); - } - } - - public boolean pathNodesExist(short regionoffset) - { - return false; - } - - public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) - { - 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)))); - if (buffer == null) - { - return null; - } - - List path = null; - try - { - final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); - - if (result == null) - { - return null; - } - - path = constructPath(result); - } - catch (Exception e) - { - LOGGER.warning(e.getMessage()); - return null; - } - finally - { - buffer.free(); - } - - // check path - if (path.size() < 3) - { - return path; - } - - int currentX, currentY, currentZ; - ListIterator middlePoint; - - middlePoint = path.listIterator(); - currentX = x; - currentY = y; - currentZ = z; - - while (middlePoint.hasNext()) - { - final AbstractNodeLoc locMiddle = middlePoint.next(); - if (!middlePoint.hasNext()) - { - break; - } - - final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); - if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) - { - middlePoint.remove(); - } - else - { - currentX = locMiddle.getX(); - currentY = locMiddle.getY(); - currentZ = locMiddle.getZ(); - } - } - - return path; - } - - /** - * Convert geodata position to pathnode position - * @param geo_pos - * @return pathnode position - */ - public short getNodePos(int geo_pos) - { - return (short) (geo_pos >> 3); // OK? - } - - /** - * Convert node position to pathnode block position - * @param node_pos - * @return pathnode block position (0...255) - */ - public short getNodeBlock(int node_pos) - { - return (short) (node_pos % 256); - } - - public byte getRegionX(int node_pos) - { - return (byte) ((node_pos >> 8) + World.TILE_X_MIN); - } - - public byte getRegionY(int node_pos) - { - return (byte) ((node_pos >> 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 node_x rx - * @return - */ - public int calculateWorldX(short node_x) - { - return World.MAP_MIN_X + (node_x * 128) + 48; - } - - /** - * Convert pathnode y to World y position - * @param node_y - * @return - */ - public int calculateWorldY(short node_y) - { - return World.MAP_MIN_Y + (node_y * 128) + 48; - } - - private List constructPath(AbstractNode nodeValue) - { - final LinkedList path = new LinkedList<>(); - int previousDirectionX = Integer.MIN_VALUE; - int previousDirectionY = Integer.MIN_VALUE; - int directionX, directionY; - - AbstractNode node = nodeValue; - while (node.getParent() != null) - { - directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); - directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); - - // only add a new route point if moving direction changes - if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) - { - previousDirectionX = directionX; - previousDirectionY = directionY; - - path.addFirst(node.getLoc()); - node.setLoc(null); - } - - node = node.getParent(); - } - - return path; - } - - private CellNodeBuffer alloc(int size) - { - CellNodeBuffer current = null; - for (BufferInfo i : _buffers) - { - if (i.mapSize >= size) - { - for (CellNodeBuffer buf : i.buffer) - { - if (buf.lock()) - { - current = buf; - break; - } - } - if (current != null) - { - break; - } - - // not found, allocate temporary buffer - current = new CellNodeBuffer(i.mapSize); - current.lock(); - if (i.buffer.size() < i.count) - { - i.buffer.add(current); - break; - } - } - } - - return current; - } - - private static final class BufferInfo - { - final int mapSize; - final int count; - ArrayList buffer; - - public BufferInfo(int size, int cnt) - { - mapSize = size; - count = cnt; - buffer = new ArrayList<>(count); - } - } - - public static GeoEnginePathfinding getInstance() - { - return SingletonHolder.INSTANCE; - } - - private static class SingletonHolder - { - protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); - } -} diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java new file mode 100644 index 0000000000..49ec35aa1c --- /dev/null +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java @@ -0,0 +1,85 @@ +/* + * 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 . + */ +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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @param index : Index of the cell. + * @return short : Cell geodata Z coordinate, below given coordinates. + */ + public abstract byte getNswe(int index); +} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java new file mode 100644 index 0000000000..f5997bf287 --- /dev/null +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java @@ -0,0 +1,130 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java new file mode 100644 index 0000000000..9af9d2a28a --- /dev/null +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java @@ -0,0 +1,95 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java new file mode 100644 index 0000000000..31fbf5d477 --- /dev/null +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java similarity index 55% rename from L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java rename to L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java index 72a5a635c7..f77d21d84b 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java @@ -16,40 +16,53 @@ */ package org.l2jmobius.gameserver.geoengine.geodata; -/** - * @author HorridoJoho - */ -public final class NullRegion implements IRegion +public class BlockNull extends ABlock { - public static final NullRegion INSTANCE = new NullRegion(); - @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 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() + public boolean hasGeoPos() { 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java deleted file mode 100644 index 61ea4fcf82..0000000000 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java +++ /dev/null @@ -1,48 +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 . - */ -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() - { - } -} diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java deleted file mode 100644 index 3c8de1a7f7..0000000000 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java +++ /dev/null @@ -1,77 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java deleted file mode 100644 index 1ccdefe3da..0000000000 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java +++ /dev/null @@ -1,56 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java new file mode 100644 index 0000000000..691b164e0c --- /dev/null +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ +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_ALL = 0x0F; + + // 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; +} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java deleted file mode 100644 index 25eafc5a04..0000000000 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java +++ /dev/null @@ -1,42 +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 . - */ -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); -} diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java deleted file mode 100644 index 0d2c765002..0000000000 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java +++ /dev/null @@ -1,47 +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 . - */ -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(); -} diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java deleted file mode 100644 index 52df457360..0000000000 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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); - - // 1 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; - } -} diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java deleted file mode 100644 index fc924cb875..0000000000 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java +++ /dev/null @@ -1,92 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java deleted file mode 100644 index 5087513ed9..0000000000 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java +++ /dev/null @@ -1,87 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public abstract class AbstractNode -{ - private T _loc; - private AbstractNode _parent; - - public AbstractNode(T loc) - { - _loc = loc; - } - - public void setParent(AbstractNode p) - { - _parent = p; - } - - public AbstractNode getParent() - { - return _parent; - } - - public T getLoc() - { - return _loc; - } - - public void setLoc(T l) - { - _loc = l; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); - return result; - } - - @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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java deleted file mode 100644 index 3425234e0e..0000000000 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public class CellNode extends AbstractNode -{ - 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java deleted file mode 100644 index 522610bb7c..0000000000 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java +++ /dev/null @@ -1,348 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -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 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) - { - _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(); - } - - public final List debugPath() - { - final List 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 void getNeighbors() - { - if (!_current.getLoc().canGoAll()) - { - 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); - } - - // SouthEast - if ((nodeE != null) && (nodeS != null)) - { - if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) - { - addNode(x + 1, y + 1, z, true); - } - } - - // SouthWest - if ((nodeS != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) - { - addNode(x - 1, y + 1, z, true); - } - } - - // NorthEast - if ((nodeN != null) && (nodeE != null)) - { - if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) - { - addNode(x + 1, y - 1, z, true); - } - } - - // NorthWest - if ((nodeN != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) - { - addNode(x - 1, y - 1, z, true); - } - } - } - - private 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(); - // reinit node if needed - if (result.getLoc() != null) - { - result.getLoc().set(x, y, z); - } - else - { - result.setLoc(new NodeLoc(x, y, z)); - } - } - - return result; - } - - private 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 boolean isHighWeight(int x, int y, int z) - { - final CellNode result = getNode(x, y, z); - if (result == null) - { - return true; - } - - if (!result.getLoc().canGoAll()) - { - return true; - } - if (Math.abs(result.getLoc().getZ() - z) > 16) - { - return true; - } - - return false; - } - - private 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java new file mode 100644 index 0000000000..fa5ca43c2f --- /dev/null +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -0,0 +1,111 @@ +/* + * 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 . + */ +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 geodata values. + private int _geoX; + private int _geoY; + private byte _nswe; + + // 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; + + _costG = 0; + _costH = 0; + _costF = 0; + + _parent = null; + } + + public void setGeo(int gx, int gy, int gz, byte nswe) + { + super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); + + _geoX = gx; + _geoY = gy; + _nswe = nswe; + } + + 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 int getCostF() + { + return _costF; + } + + public Node getParent() + { + return _parent; + } + + @Override + public int compareTo(Node o) + { + return _costF - o._costF; + } +} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java new file mode 100644 index 0000000000..b464212c96 --- /dev/null +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -0,0 +1,368 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.awt.Color; +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; +import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; + +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 _opened; + // Container holding Nodes already explored. + private final List _closed; + + // Target coordinates. + private int _gtx; + private int _gty; + private int _gtz; + + // Pathfinding statistics. + private long _timeStamp; + private long _lastElapsedTime; + + 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 findPath(int gox, int goy, int goz, int gtx, int gty, int gtz) + { + // Set start timestamp. + _timeStamp = System.currentTimeMillis(); + + // 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)); + _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 constructPath() + { + // Create result. + final LinkedList 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; + } + + /** + * Creates list of Nodes to show debug path. + * @param debug : The debug packet to add debug informations in. + */ + public void debugPath(ExServerPrimitive debug) + { + // Add all opened node as yellow points. + for (Node n : _opened) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); + } + + // Add all opened node as blue points. + for (Node n : _closed) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); + } + } + + 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; + + _lastElapsedTime = System.currentTimeMillis() - _timeStamp; + _lock.unlock(); + } + + public long getElapsedTime() + { + return _lastElapsedTime; + } + + /** + * Expand the current {@link Node} by exploring its neighbors (axially and diagonally). + */ + private void expand() + { + // Movement is blocked, skip. + final byte nswe = _current.getNSWE(); + if (nswe == 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; + + // Can move north, expand. + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + } + + // Can move south, expand. + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + } + + // Can move west, expand. + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move east, expand. + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move north-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move north-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + } + + /** + * 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 weight : The weight of movement to the new node. + * @return The nswe of the added node. Blank, if not added. + */ + private byte addNode(int gx, int gy, int gzValue, int weight) + { + // 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]; + + // Set node geodata coordinates. + node.setGeo(gx, gy, gz, nswe); + + // 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, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + _opened.add(node); + _bufferIndex++; + return nswe; + } + + /** + * Calculate cost H value, calculated using diagonal distance method.
+ * 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. + final int dx = Math.abs(gx - _gtx); + final int dy = Math.abs(gy - _gty); + final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + + // Get diagonal and axial differences to the target. + final int dd = Math.min(dx, dy); + final int da = Math.max(dx, dy) - dd; + + // Calculate the diagonal distance of the node to the target. + return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + } +} \ No newline at end of file diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java deleted file mode 100644 index e3bad04b25..0000000000 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; - -/** - * @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 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 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; - // return super.hashCode(); - } - - @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; - if (_x != other._x) - { - return false; - } - if (_y != other._y) - { - return false; - } - if (_goNorth != other._goNorth) - { - return false; - } - if (_goEast != other._goEast) - { - return false; - } - if (_goSouth != other._goSouth) - { - return false; - } - if (_goWest != other._goWest) - { - return false; - } - if (_geoHeight != other._geoHeight) - { - return false; - } - return true; - } -} diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java index 1faf1ae423..ed261765e1 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java @@ -93,15 +93,15 @@ public class ZoneManager implements IXmlReader private static final Map SETTINGS = new HashMap<>(); private static final int SHIFT_BY = 15; - private static final int OFFSET_X = Math.abs(World.MAP_MIN_X >> SHIFT_BY); - private static final int OFFSET_Y = Math.abs(World.MAP_MIN_Y >> SHIFT_BY); + private static final int OFFSET_X = Math.abs(World.WORLD_X_MIN >> SHIFT_BY); + private static final int OFFSET_Y = Math.abs(World.WORLD_Y_MIN >> SHIFT_BY); private final Map, ConcurrentHashMap> _classZones = new ConcurrentHashMap<>(); private final Map _spawnTerritories = new ConcurrentHashMap<>(); private final AtomicInteger _lastDynamicId = new AtomicInteger(300000); private List _debugItems; - private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.MAP_MAX_X >> SHIFT_BY) + OFFSET_X + 1][(World.MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y + 1]; + private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.WORLD_X_MAX >> SHIFT_BY) + OFFSET_X + 1][(World.WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y + 1]; /** * Instantiates a new zone manager. diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/Location.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/Location.java index 73689ab1a6..ca3b22a3f0 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/Location.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/Location.java @@ -16,30 +16,30 @@ */ package org.l2jmobius.gameserver.model; +import java.util.Objects; + +import org.l2jmobius.commons.util.Point2D; import org.l2jmobius.gameserver.model.interfaces.ILocational; import org.l2jmobius.gameserver.model.interfaces.IPositionable; /** - * Location data transfer object.
- * Contains coordinates data, heading and instance Id. - * @author Zoey76 + * A datatype used to retain a 3D (x/y/z/heading) point. It got the capability to be set and cleaned. */ -public class Location implements IPositionable +public class Location extends Point2D implements IPositionable { - protected int _x; - protected int _y; - protected int _z; - private int _heading; + protected volatile int _z; + protected volatile int _heading; public Location(int x, int y, int z) { - this(x, y, z, 0); + super(x, y); + _z = z; + _heading = 0; } public Location(int x, int y, int z, int heading) { - _x = x; - _y = y; + super(x, y); _z = z; _heading = heading; } @@ -51,9 +51,8 @@ public class Location implements IPositionable public Location(StatSet set) { - _x = set.getInt("x"); - _y = set.getInt("y"); - _z = set.getInt("z"); + super(set.getInt("x", 0), set.getInt("y", 0)); + _z = set.getInt("z", 0); _heading = set.getInt("heading", 0); } @@ -146,6 +145,25 @@ public class Location implements IPositionable _heading = loc.getHeading(); } + @Override + public void clean() + { + super.clean(); + _z = 0; + } + + @Override + public Location clone() + { + return new Location(_x, _y, _z); + } + + @Override + public int hashCode() + { + return (31 * super.hashCode()) + Objects.hash(_z); + } + @Override public boolean equals(Object obj) { diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/World.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/World.java index c1510e8c0e..0d796dd834 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/World.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/World.java @@ -66,19 +66,19 @@ public class World public static final int TILE_Y_MAX = 26; public static final int TILE_ZERO_COORD_X = 20; public static final int TILE_ZERO_COORD_Y = 18; - public static final int MAP_MIN_X = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; - public static final int MAP_MIN_Y = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; + public static final int WORLD_X_MIN = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; + public static final int WORLD_Y_MIN = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; - public static final int MAP_MAX_X = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; - public static final int MAP_MAX_Y = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; + public static final int WORLD_X_MAX = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; + public static final int WORLD_Y_MAX = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; /** Calculated offset used so top left region is 0,0 */ - public static final int OFFSET_X = Math.abs(MAP_MIN_X >> SHIFT_BY); - public static final int OFFSET_Y = Math.abs(MAP_MIN_Y >> SHIFT_BY); + public static final int OFFSET_X = Math.abs(WORLD_X_MIN >> SHIFT_BY); + public static final int OFFSET_Y = Math.abs(WORLD_Y_MIN >> SHIFT_BY); /** Number of regions. */ - private static final int REGIONS_X = (MAP_MAX_X >> SHIFT_BY) + OFFSET_X; - private static final int REGIONS_Y = (MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y; + private static final int REGIONS_X = (WORLD_X_MAX >> SHIFT_BY) + OFFSET_X; + private static final int REGIONS_Y = (WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y; /** Map containing all the players in game. */ private static final Map _allPlayers = new ConcurrentHashMap<>(); @@ -817,9 +817,6 @@ public class World return _memberInPartyNumber.get(); } - /** - * @return the current instance of World - */ public static World getInstance() { return SingletonHolder.INSTANCE; diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/WorldObject.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/WorldObject.java index 0e5536bbc0..62313ffea1 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/WorldObject.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/WorldObject.java @@ -188,23 +188,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif synchronized (this) { int spawnX = x; - if (spawnX > World.MAP_MAX_X) + if (spawnX > World.WORLD_X_MAX) { - spawnX = World.MAP_MAX_X - 5000; + spawnX = World.WORLD_X_MAX - 5000; } - if (spawnX < World.MAP_MIN_X) + if (spawnX < World.WORLD_X_MIN) { - spawnX = World.MAP_MIN_X + 5000; + spawnX = World.WORLD_X_MIN + 5000; } int spawnY = y; - if (spawnY > World.MAP_MAX_Y) + if (spawnY > World.WORLD_Y_MAX) { - spawnY = World.MAP_MAX_Y - 5000; + spawnY = World.WORLD_Y_MAX - 5000; } - if (spawnY < World.MAP_MIN_Y) + if (spawnY < World.WORLD_Y_MIN) { - spawnY = World.MAP_MIN_Y + 5000; + spawnY = World.WORLD_Y_MIN + 5000; } // Set the x,y,z position of the WorldObject. If flagged with _isSpawned, setXYZ will automatically update world region, so avoid that. @@ -518,23 +518,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif public void setXYZInvisible(int x, int y, int z) { int correctX = x; - if (correctX > World.MAP_MAX_X) + if (correctX > World.WORLD_X_MAX) { - correctX = World.MAP_MAX_X - 5000; + correctX = World.WORLD_X_MAX - 5000; } - if (correctX < World.MAP_MIN_X) + if (correctX < World.WORLD_X_MIN) { - correctX = World.MAP_MIN_X + 5000; + correctX = World.WORLD_X_MIN + 5000; } int correctY = y; - if (correctY > World.MAP_MAX_Y) + if (correctY > World.WORLD_Y_MAX) { - correctY = World.MAP_MAX_Y - 5000; + correctY = World.WORLD_Y_MAX - 5000; } - if (correctY < World.MAP_MIN_Y) + if (correctY < World.WORLD_Y_MIN) { - correctY = World.MAP_MIN_Y + 5000; + correctY = World.WORLD_Y_MIN + 5000; } setXYZ(correctX, correctY, z); diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/actor/Creature.java index e0c108de41..33cdefc41d 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -65,8 +65,6 @@ 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.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -2585,7 +2583,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3406,8 +3404,8 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe final int originalX = x; final int originalY = y; final int originalZ = z; - final int gtx = (originalX - World.MAP_MIN_X) >> 4; - final int gty = (originalY - World.MAP_MIN_Y) >> 4; + final int gtx = (originalX - World.WORLD_X_MIN) >> 4; + final int gty = (originalY - World.WORLD_Y_MIN) >> 4; if (isOnGeodataPath()) { try @@ -3430,7 +3428,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe && !(((curZ - z) > 300) && (distance < 300))) // Prohibit correcting destination if character wants to fall. { // location different if destination wasn't reached (or just z coord is different) - final Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(curX, curY, curZ, x, y, z, getInstanceWorld()); + final Location destiny = GeoEngine.getInstance().getValidLocation(curX, curY, curZ, x, y, z, getInstanceWorld()); x = destiny.getX(); y = destiny.getY(); z = destiny.getZ(); @@ -3444,7 +3442,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { if (isPlayer() && !_isFlying && !isInWater) diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/actor/Summon.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/actor/Summon.java index 6065b24f61..919cda941e 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/actor/Summon.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/actor/Summon.java @@ -106,7 +106,7 @@ public abstract class Summon extends Playable final int x = owner.getX(); final int y = owner.getY(); final int z = owner.getZ(); - final Location location = GeoEngine.getInstance().canMoveToTargetLoc(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, owner.getInstanceWorld()); + final Location location = GeoEngine.getInstance().getValidLocation(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, getInstanceWorld()); setXYZInvisible(location.getX(), location.getY(), location.getZ()); } diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java index 71b6dde756..c3b6b052fc 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java @@ -1507,7 +1507,7 @@ public class ItemInstance extends WorldObject if (dropper != null) { final Instance instance = dropper.getInstanceWorld(); - final Location dropDest = GeoEngine.getInstance().canMoveToTargetLoc(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); + final Location dropDest = GeoEngine.getInstance().getValidLocation(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); x = dropDest.getX(); y = dropDest.getY(); z = dropDest.getZ(); diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java index 4771ad5957..ef1fd785d7 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java @@ -1179,7 +1179,7 @@ public class SkillCaster implements Runnable } } - final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().canMoveToTargetLoc(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); + final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().getValidLocation(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); creature.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); creature.broadcastPacket(new FlyToLocation(creature, destination, flyType, 0, 0, 333)); diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java index c62c821f44..30068134e3 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java @@ -114,7 +114,7 @@ public class ValidatePosition implements IClientIncomingPacket { if (player.isFalling(_z)) { - final int nearestZ = GeoEngine.getInstance().getHigherHeight(_x, _y, _z); + final int nearestZ = GeoEngine.getInstance().getHeight(_x, _y, _z); if (player.getZ() < nearestZ) { player.setXYZ(_x, _y, nearestZ); diff --git a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/util/GeoUtils.java index 4600a0fc53..19a979819e 100644 --- a/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_3.0_Helios/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -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.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; @@ -30,21 +30,21 @@ public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getInstance().getWorldX(iter.x()); - final int wy = GeoEngine.getInstance().getWorldY(iter.y()); + final int wx = GeoEngine.getWorldX(iter.x()); + final int wy = GeoEngine.getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public final class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); - int wy = GeoEngine.getInstance().getWorldY(prevY); + int wx = GeoEngine.getWorldX(prevX); + int wy = GeoEngine.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.getInstance().getWorldX(curX); - wy = GeoEngine.getInstance().getWorldY(curY); + wx = GeoEngine.getWorldX(curX); + wy = GeoEngine.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().checkNearestNswe(x, y, z, nswe)) + if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0) { return Color.GREEN; } @@ -109,9 +109,8 @@ public final class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final GeoEngine ge = GeoEngine.getInstance(); - final int playerGx = ge.getGeoX(player.getX()); - final int playerGy = ge.getGeoY(player.getY()); + final int playerGx = GeoEngine.getGeoX(player.getX()); + final int playerGy = GeoEngine.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -135,32 +134,32 @@ public final class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = ge.getWorldX(gx); - final int y = ge.getWorldY(gy); - final int z = ge.getNearestZ(gx, gy, player.getZ()); + final int x = GeoEngine.getWorldX(gx); + final int y = GeoEngine.getWorldY(gy); + final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); + Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); 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, Cell.NSWE_EAST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); 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, Cell.NSWE_SOUTH); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); 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, Cell.NSWE_WEST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); 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); @@ -188,41 +187,41 @@ public final class GeoUtils { if (y > lastY) { - return Cell.NSWE_SOUTH_EAST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E; } else if (y < lastY) { - return Cell.NSWE_NORTH_EAST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E; } else { - return Cell.NSWE_EAST; + return GeoStructure.CELL_FLAG_E; } } else if (x < lastX) // west { if (y > lastY) { - return Cell.NSWE_SOUTH_WEST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W; } else if (y < lastY) { - return Cell.NSWE_NORTH_WEST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W; } else { - return Cell.NSWE_WEST; + return GeoStructure.CELL_FLAG_W; } } else // unchanged x { if (y > lastY) { - return Cell.NSWE_SOUTH; + return GeoStructure.CELL_FLAG_S; } else if (y < lastY) { - return Cell.NSWE_NORTH; + return GeoStructure.CELL_FLAG_N; } else { diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/config/GeoEngine.ini b/L2J_Mobius_4.0_GrandCrusade/dist/game/config/GeoEngine.ini index 609e8a89a7..e740c678dd 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/config/GeoEngine.ini @@ -6,33 +6,44 @@ # at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ 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 + # ================================================================= -# Path finding +# Pathfinding # ================================================================= # 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 +# an alternative path (e.g. walk around obstacle), default: True +PathFinding = True -# Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 -PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 +# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 -# Weight for nodes without obstacles far from walls -LowWeight = 0.5 +# Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 +MoveWeight = 10 +MoveWeightDiag = 14 -# Weight for nodes near walls -MediumWeight = 2 +# 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 -# Weight for nodes with obstacles -HighWeight = 3 +# 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. -# Default: LowWeight * sqrt(2) -DiagonalWeight = 0.707 +# Maximum number of generated nodes per one path-finding process, default 3500 +MaxIterations = 3500 # ================================================================= -# Other +# Line of Sight # ================================================================= -# Correct player Z after falling through the ground. -CorrectPlayerZ = False +# 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 diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/geodata/L2D_GeoEngine.patch b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/geodata/L2D_GeoEngine.patch deleted file mode 100644 index f8577e3a0e..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/geodata/L2D_GeoEngine.patch +++ /dev/null @@ -1,6035 +0,0 @@ -Index: dist/game/GeoDataConverter.bat -=================================================================== ---- dist/game/GeoDataConverter.bat (nonexistent) -+++ dist/game/GeoDataConverter.bat (working copy) -@@ -0,0 +1,6 @@ -+@echo off -+title L2D geodata converter -+ -+java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter -+ -+pause -Index: dist/game/GeoDataConverter.sh -=================================================================== ---- dist/game/GeoDataConverter.sh (nonexistent) -+++ dist/game/GeoDataConverter.sh (working copy) -@@ -0,0 +1,4 @@ -+#! /bin/sh -+ -+java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 -+ -Index: dist/game/config/GeoEngine.ini -=================================================================== ---- dist/game/config/GeoEngine.ini (revision 8409) -+++ dist/game/config/GeoEngine.ini (working copy) -@@ -1,6 +1,10 @@ - # ================================================================= - # Geodata - # ================================================================= -+# Because of real-time performance we are using geodata files only in -+# diagonal L2D format now (using filename e.g. 22_16.l2d). -+# L2D geodata can be obtained by conversion of existing L2J or L2OFF geodata. -+# Launch "GeoDataConverter.bat/sh" and follow instructions to start the conversion. - - # 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/ -@@ -13,6 +17,16 @@ - CoordSynchronize = 2 - - # ================================================================= -+# Path checking -+# ================================================================= -+ -+# 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 finding - # ================================================================= - -@@ -23,19 +37,23 @@ - # Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - --# Weight for nodes without obstacles far from walls --LowWeight = 0.5 -+# Base path weight, when moving from one node to another on axis direction, default: 10 -+BaseWeight = 10 - --# Weight for nodes near walls --MediumWeight = 2 -+# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -+DiagonalWeight = 14 - --# Weight for nodes with obstacles --HighWeight = 3 -+# When movement flags of target node is blocked to any direction, multiply movement weight by this multiplier. -+# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 10 -+ObstacleMultiplier = 10 - --# Weight for diagonal movement. --# Default: LowWeight * sqrt(2) --DiagonalWeight = 0.707 -+# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 20 -+# For proper function must be higher than BaseWeight and/or DiagonalWeight. -+HeuristicWeight = 20 - -+# Maximum number of generated nodes per one path-finding process, default 3500 -+MaxIterations = 3500 -+ - # ================================================================= - # Other - # ================================================================= -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (working copy) -@@ -55,9 +55,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -@@ -73,9 +72,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (working copy) -@@ -18,11 +18,11 @@ - - import java.util.List; - --import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -+import org.l2jmobius.gameserver.geoengine.GeoEngine; - import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -+import org.l2jmobius.gameserver.network.SystemMessageId; - import org.l2jmobius.gameserver.util.BuilderUtil; - - public class AdminPathNode implements IAdminCommandHandler -@@ -37,29 +37,30 @@ - { - if (command.equals("admin_path_find")) - { -- if (!Config.PATHFINDING) -- { -- BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); -- return true; -- } - if (activeChar.getTarget() != null) - { -- final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); -+ final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); - if (path == null) - { -- BuilderUtil.sendSysMessage(activeChar, "No Route!"); -- return true; -+ BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); - } -- for (AbstractNodeLoc a : path) -+ else - { -- BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); -+ for (Location point : path) -+ { -+ BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); -+ } - } - } - else - { -- BuilderUtil.sendSysMessage(activeChar, "No Target!"); -+ activeChar.sendPacket(SystemMessageId.INVALID_TARGET); - } - } -+ else -+ { -+ return false; -+ } - return true; - } - -Index: java/org/l2jmobius/Config.java -=================================================================== ---- java/org/l2jmobius/Config.java (revision 8409) -+++ java/org/l2jmobius/Config.java (working copy) -@@ -32,7 +32,6 @@ - import java.net.UnknownHostException; - import java.nio.charset.StandardCharsets; - import java.nio.file.Files; --import java.nio.file.Path; - import java.nio.file.Paths; - import java.time.Duration; - import java.util.ArrayList; -@@ -966,14 +965,17 @@ - // -------------------------------------------------- - // GeoEngine - // -------------------------------------------------- -- public static Path GEODATA_PATH; -+ public static String GEODATA_PATH; -+ public static int COORD_SYNCHRONIZE; -+ public static int PART_OF_CHARACTER_HEIGHT; -+ public static int MAX_OBSTACLE_HEIGHT; - public static boolean PATHFINDING; - public static String PATHFIND_BUFFERS; -- public static float LOW_WEIGHT; -- public static float MEDIUM_WEIGHT; -- public static float HIGH_WEIGHT; -- public static float DIAGONAL_WEIGHT; -- public static int COORD_SYNCHRONIZE; -+ public static int BASE_WEIGHT; -+ public static int DIAGONAL_WEIGHT; -+ public static int HEURISTIC_WEIGHT; -+ public static int OBSTACLE_MULTIPLIER; -+ public static int MAX_ITERATIONS; - public static boolean CORRECT_PLAYER_Z; - - /** Attribute System */ -@@ -2568,14 +2570,17 @@ - - // Load GeoEngine config file (if exists) - final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); -- GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); -+ GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); -+ COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); -+ MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); - PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -- LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); -- MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); -- HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); -- DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); -- COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); -+ DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); -+ OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); -+ HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); -+ MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); - CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); - - // Load AllowedPlayerRaces config file (if exists) -Index: java/org/l2jmobius/gameserver/geoengine/GeoEngine.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (revision 8435) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (working copy) -@@ -16,100 +16,90 @@ - */ - package org.l2jmobius.gameserver.geoengine; - --import java.io.IOException; -+import java.io.File; - import java.io.RandomAccessFile; - import java.nio.ByteOrder; --import java.nio.channels.FileChannel.MapMode; --import java.nio.file.Files; --import java.nio.file.Path; --import java.util.concurrent.atomic.AtomicReferenceArray; --import java.util.logging.Level; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.List; - import java.util.logging.Logger; - - import org.l2jmobius.Config; - import org.l2jmobius.gameserver.data.xml.DoorData; - import org.l2jmobius.gameserver.data.xml.FenceData; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; --import org.l2jmobius.gameserver.geoengine.geodata.IRegion; --import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; --import org.l2jmobius.gameserver.geoengine.geodata.Region; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.World; - import org.l2jmobius.gameserver.model.WorldObject; -+import org.l2jmobius.gameserver.model.actor.Creature; - import org.l2jmobius.gameserver.model.instancezone.Instance; --import org.l2jmobius.gameserver.model.interfaces.ILocational; --import org.l2jmobius.gameserver.util.GeoUtils; --import org.l2jmobius.gameserver.util.LinePointIterator; --import org.l2jmobius.gameserver.util.LinePointIterator3D; -+import org.l2jmobius.gameserver.util.MathUtil; - - /** -- * @author -Nemesiss-, HorridoJoho -+ * @author Hasha - */ - public class GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); -+ protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - -- private static final int WORLD_MIN_X = -655360; -- private static final int WORLD_MIN_Y = -589824; -- private static final int WORLD_MIN_Z = -16384; -+ private final ABlock[][] _blocks; -+ private final BlockNull _nullBlock; - -- /** 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; -- -- /** The regions array */ -- private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); -- -- private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; -- private static final int ELEVATED_SEE_OVER_DISTANCE = 2; -- private static final int MAX_SEE_OVER_HEIGHT = 48; -- -- protected GeoEngine() -+ /** -+ * GeoEngine constructor. Loads all geodata files of chosen geodata format. -+ */ -+ public GeoEngine() - { - LOGGER.info("GeoEngine: Initializing..."); -- for (int i = 0; i < _regions.length(); i++) -- { -- _regions.set(i, NullRegion.INSTANCE); -- } - -+ // initialize block container -+ _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; -+ -+ // load null block -+ _nullBlock = new BlockNull(); -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files according to geoengine config setup - int loaded = 0; -- try -+ long fileSize = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { -- for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { -- for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) -+ final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); -+ if (f.exists() && !f.isDirectory()) - { -- final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); -- if (Files.exists(geoFilePath)) -+ // region file is load-able, try to load it -+ if (loadGeoBlocks(rx, ry)) - { -- try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) -- { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -- loaded++; -- } -+ loaded++; -+ fileSize += f.length(); - } - } -+ else -+ { -+ // region file is not load-able, load null blocks -+ loadNullBlocks(rx, ry); -+ } - } - } -- catch (Exception e) -+ -+ LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -+ if (loaded > 0) - { -- LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); -- System.exit(1); -+ LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); - } - -- LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -- -- // Avoid wrong configs when no files are loaded. -+ // avoid wrong configs when no files are loaded - if (loaded == 0) - { - if (Config.PATHFINDING) -@@ -123,619 +113,820 @@ - LOGGER.info("GeoEngine: Forcing CoordSynchronize setting to -1."); - } - } -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); - } - - /** -- * @param geoX -- * @param geoY -- * @return the region -+ * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. -+ * @return boolean : True, when geodata file was loaded without problem. - */ -- private IRegion getRegion(int geoX, int geoY) -+ private final boolean loadGeoBlocks(int regionX, int regionY) - { -- final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); -- if ((region < 0) || (region >= _regions.length())) -+ final String filename = String.format(GeoFormat.L2D.getFilename(), regionX, regionY); -+ -+ // standard load -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) - { -- return null; -+ // initialize file buffer -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, GeoFormat.L2D); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ } -+ -+ // check data consistency -+ if (buffer.remaining() > 0) -+ { -+ LOGGER.warning("GeoEngine: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ } -+ -+ // loading was successful -+ return true; - } -- return _regions.get(region); -- } -- -- /** -- * @param filePath -- * @param regionX -- * @param regionY -- * @throws IOException -- */ -- 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")) -+ catch (Exception e) - { -- _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -+ // an error occured while loading, load null blocks -+ LOGGER.warning("GeoEngine: Error while loading " + filename + " region file."); -+ LOGGER.warning(e.getMessage()); -+ -+ // replace whole region file with null blocks -+ loadNullBlocks(regionX, regionY); -+ -+ // loading was not successful -+ return false; - } - } - - /** -- * @param regionX -- * @param regionY -+ * Loads null blocks. Used when no region file is detected or an error occurs during loading. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. - */ -- public void unloadRegion(int regionX, int regionY) -+ private final void loadNullBlocks(int regionX, int regionY) - { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); -- } -- -- /** -- * @param geoX -- * @param geoY -- * @return if geodata exist -- */ -- public boolean hasGeoPos(int geoX, int geoY) -- { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // load all null blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { -- return false; -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[blockX + ix][blockY + iy] = _nullBlock; -+ } - } -- return region.hasGeo(); - } - -+ // GEODATA - GENERAL -+ - /** -- * Checks the specified position for available geodata. -- * @param x the world x -- * @param y the world y -- * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise -+ * Converts world X to geodata X. -+ * @param worldX -+ * @return int : Geo X - */ -- public boolean hasGeo(int x, int y) -+ public static int getGeoX(int worldX) - { -- return hasGeoPos(getGeoX(x), getGeoY(y)); -+ return (MathUtil.limit(worldX, World.MAP_MIN_X, World.MAP_MAX_X) - World.MAP_MIN_X) >> 4; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe check -+ * Converts world Y to geodata Y. -+ * @param worldY -+ * @return int : Geo Y - */ -- public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) -+ public static int getGeoY(int worldY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return true; -- } -- return region.checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(worldY, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.MAP_MIN_Y) >> 4; - } - - /** -+ * Converts geodata X to world X. - * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe anti-corner cut -+ * @return int : World X - */ -- public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) -+ public static int getWorldX(int geoX) - { -- boolean can = true; -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- return can && checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(geoX, 0, GeoStructure.GEO_CELLS_X) << 4) + World.MAP_MIN_X + 8; - } - - /** -- * @param geoX -+ * Converts geodata Y to world Y. - * @param geoY -- * @param worldZ -- * @return the nearest Z value -+ * @return int : World Y - */ -- public int getNearestZ(int geoX, int geoY, int worldZ) -+ public static int getWorldY(int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return worldZ; -- } -- return region.getNearestZ(geoX, geoY, worldZ); -+ return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next lower Z value -+ * Returns block of geodata on given coordinates. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return {@link ABlock} : Block of geodata. - */ -- public int getNextLowerZ(int geoX, int geoY, int worldZ) -+ private final ABlock getBlock(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final int x = geoX / GeoStructure.BLOCK_CELLS_X; -+ final int y = geoY / GeoStructure.BLOCK_CELLS_Y; -+ -+ // if x or y is out of array return null -+ if ((x > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) - { -- return worldZ; -+ return _blocks[x][y]; - } -- return region.getNextLowerZ(geoX, geoY, worldZ); -+ return null; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next higher Z value -+ * Check if geo coordinates has geo. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return boolean : True, if given geo coordinates have geodata - */ -- public int getNextHigherZ(int geoX, int geoY, int worldZ) -+ public boolean hasGeoPos(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final ABlock block = getBlock(geoX, geoY); -+ if (block == null) // null block check - { -- return worldZ; -+ // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) -+ // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); -+ return false; - } -- return region.getNextHigherZ(geoX, geoY, worldZ); -+ return block.hasGeoPos(); - } - - /** -- * Gets the Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the height of cell, which is closest to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, closest to given coordinates. - */ -- public int getHeight(int x, int y, int z) -+ public short getHeightNearest(int geoX, int geoY, int worldZ) - { -- return getNearestZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getHeightNearest(geoX, geoY, worldZ) : (short) worldZ; - } - - /** -- * Gets the next lower Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the NSWE flag byte of cell, which is closes to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. - */ -- public int getLowerHeight(int x, int y, int z) -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) - { -- return getNextLowerZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getNsweNearest(geoX, geoY, worldZ) : (byte) 0xFF; - } - - /** -- * Gets the next higher Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Check if world coordinates has geo. -+ * @param worldX : World X -+ * @param worldY : World Y -+ * @return boolean : True, if given world coordinates have geodata - */ -- public int getHigherHeight(int x, int y, int z) -+ public boolean hasGeo(int worldX, int worldY) - { -- return getNextHigherZ(getGeoX(x), getGeoY(y), z); -+ return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); - } - - /** -- * @param worldX -- * @return the geo X -+ * Returns closest Z coordinate according to geodata. -+ * @param worldX : world x -+ * @param worldY : world y -+ * @param worldZ : world z -+ * @return short : nearest Z coordinates according to geodata - */ -- public int getGeoX(int worldX) -+ public short getHeight(int worldX, int worldY, int worldZ) - { -- return (worldX - WORLD_MIN_X) / 16; -+ return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - -- /** -- * @param worldY -- * @return the geo Y -- */ -- public int getGeoY(int worldY) -- { -- return (worldY - WORLD_MIN_Y) / 16; -- } -+ // PATHFINDING - - /** -- * @param worldZ -- * @return the geo Z -+ * Check line of sight from {@link WorldObject} to {@link WorldObject}. -+ * @param origin : The origin object. -+ * @param target : The target object. -+ * @return {@code boolean} : True if origin can see target - */ -- public int getGeoZ(int worldZ) -+ public boolean canSeeTarget(WorldObject origin, WorldObject target) - { -- return (worldZ - WORLD_MIN_Z) / 16; -- } -- -- /** -- * @param geoX -- * @return the world X -- */ -- public int getWorldX(int geoX) -- { -- return (geoX * 16) + WORLD_MIN_X + 8; -- } -- -- /** -- * @param geoY -- * @return the world Y -- */ -- public int getWorldY(int geoY) -- { -- return (geoY * 16) + WORLD_MIN_Y + 8; -- } -- -- /** -- * @param geoZ -- * @return the world Z -- */ -- public int getWorldZ(int geoZ) -- { -- return (geoZ * 16) + WORLD_MIN_Z + 8; -- } -- -- /** -- * Can see target. Doors as target always return true. Checks doors between. -- * @param cha the character -- * @param target the target -- * @return {@code true} if the character can see the target (LOS), {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, WorldObject target) -- { -- if (target.isDoor()) -+ if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) - { -- // Can always see doors. - return true; - } -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param cha the character -- * @param worldPosition the world position -- * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) -- { -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param world -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @param tworld the target's instanceId -- * @return -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) -- { -- return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instance -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) -- { -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) -+ -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = target.getX(); -+ final int ty = target.getY(); -+ final int tz = target.getZ(); -+ -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } -- return canSeeTarget(x, y, z, tx, ty, tz); -- } -- -- /** -- * @param prevX -- * @param prevY -- * @param prevGeoZ -- * @param curX -- * @param curY -- * @param nswe -- * @return the LOS Z value -- */ -- private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) -- { -- if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { -- throw new RuntimeException("Multiple directions!"); -+ return false; - } - -- if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- return getNearestZ(curX, curY, prevGeoZ); -+ return true; - } - -- return getNextHigherZ(curX, curY, prevGeoZ); -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) -+ { -+ return true; -+ } -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) -+ { -+ return goz == gtz; -+ } -+ -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) -+ { -+ oheight = ((Creature) origin).getCollisionHeight() * 2; -+ } -+ -+ double theight = 0; -+ if (target.isCreature()) -+ { -+ theight = ((Creature) target).getCollisionHeight() * 2; -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); - } - - /** -- * Can see target. Does not check doors between. -- * @param xValue the x coordinate -- * @param yValue the y coordinate -- * @param zValue the z coordinate -- * @param txValue the target's x coordinate -- * @param tyValue the target's y coordinate -- * @param tzValue the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -+ * Check line of sight from {@link WorldObject} to {@link Location}. -+ * @param origin : The origin object. -+ * @param position : The target position. -+ * @return {@code boolean} : True if object can see position - */ -- public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) -+ public boolean canSeeTarget(WorldObject origin, Location position) - { -- int x = xValue; -- int y = yValue; -- int tx = txValue; -- int ty = tyValue; -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = position.getX(); -+ final int ty = position.getY(); -+ final int tz = position.getZ(); - -- int geoX = getGeoX(x); -- int geoY = getGeoY(y); -- int tGeoX = getGeoX(tx); -- int tGeoY = getGeoY(ty); -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) -+ { -+ return false; -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) -+ { -+ return false; -+ } - -- int z = getNearestZ(geoX, geoY, zValue); -- int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- // Fastpath. -- if ((geoX == tGeoX) && (geoY == tGeoY)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- if (hasGeoPos(tGeoX, tGeoY)) -- { -- return z == tz; -- } - return true; - } - -- if (tz > z) -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) - { -- int tmp = tx; -- tx = x; -- x = tmp; -- -- tmp = ty; -- ty = y; -- y = tmp; -- -- tmp = tz; -- tz = z; -- z = tmp; -- -- tmp = tGeoX; -- tGeoX = geoX; -- geoX = tmp; -- -- tmp = tGeoY; -- tGeoY = geoY; -- geoY = tmp; -+ return goz == gtz; - } - -- final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); -- // First point is guaranteed to be available, skip it, we can always see our own position. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- final int prevZ = pointIter.z(); -- int prevGeoZ = prevZ; -- int ptIndex = 0; -- while (pointIter.next()) -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight(); -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); -+ } -+ -+ /** -+ * Simple check for origin to target visibility. -+ * @param goxValue : origin X geodata coordinate -+ * @param goyValue : origin Y geodata coordinate -+ * @param gozValue : origin Z geodata coordinate -+ * @param oheight : origin height (if instance of {@link Character}) -+ * @param gtxValue : target X geodata coordinate -+ * @param gtyValue : target Y geodata coordinate -+ * @param gtzValue : target Z geodata coordinate -+ * @param theight : target height (if instance of {@link Character}) -+ * @param instance -+ * @return {@code boolean} : True, when target can be seen. -+ */ -+ private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) -+ { -+ int goz = gozValue; -+ int gtz = gtzValue; -+ int gox = goxValue; -+ int goy = goyValue; -+ int gtx = gtxValue; -+ int gty = gtyValue; -+ -+ // get line of sight Z coordinates -+ double losoz = goz + ((oheight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ double lostz = gtz + ((theight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ -+ // get X delta and signum -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirox = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; -+ final byte dirtx = sx > 0 ? GeoStructure.CELL_FLAG_W : GeoStructure.CELL_FLAG_E; -+ -+ // get Y delta and signum -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte diroy = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ final byte dirty = sy > 0 ? GeoStructure.CELL_FLAG_N : GeoStructure.CELL_FLAG_S; -+ -+ // get Z delta -+ final int dm = Math.max(dx, dy); -+ final double dz = (lostz - losoz) / dm; -+ -+ // get direction flag for diagonal movement -+ final byte diroxy = getDirXY(dirox, diroy); -+ final byte dirtxy = getDirXY(dirtx, dirty); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte diro; -+ byte dirt; -+ -+ // initialize node values -+ int nox = gox; -+ int noy = goy; -+ int ntx = gtx; -+ int nty = gty; -+ byte nsweo = getNsweNearest(gox, goy, goz); -+ byte nswet = getNsweNearest(gtx, gty, gtz); -+ -+ // loop -+ ABlock block; -+ int index; -+ for (int i = 0; i < ((dm + 1) / 2); i++) -+ { -+ // reset direction flag -+ diro = 0; -+ dirt = 0; - -- if ((curX == prevX) && (curY == prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- continue; -+ // calculate next point XY coordinates -+ d -= dy; -+ d += dx; -+ nox += sx; -+ ntx -= sx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroxy; -+ dirt |= dirtxy; - } -+ else if (e2 > -dy) -+ { -+ // calculate next point X coordinate -+ d -= dy; -+ nox += sx; -+ ntx -= sx; -+ diro |= dirox; -+ dirt |= dirtx; -+ } -+ else if (e2 < dx) -+ { -+ // calculate next point Y coordinate -+ d += dx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroy; -+ dirt |= dirty; -+ } - -- final int beeCurZ = pointIter.z(); -- int curGeoZ = prevGeoZ; -- -- // Check if the position has geodata. -- if (hasGeoPos(curX, curY)) - { -- final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); -- curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); -- int maxHeight; -- if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) -+ // get block of the next cell -+ block = getBlock(nox, noy); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nsweo & diro) == 0) - { -- maxHeight = z + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); - } - else - { -- maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); - } - -- boolean canSeeThrough = false; -- if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) -+ // layer does not exist, return -+ if (index == -1) - { -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else -- { -- canSeeThrough = true; -- } -+ return false; - } - -- if (!canSeeThrough) -+ // get layer and next line of sight Z coordinate -+ goz = block.getHeight(index); -+ losoz += dz; -+ -+ // perform line of sight check, return when fails -+ if ((goz - losoz) > Config.MAX_OBSTACLE_HEIGHT) - { - return false; - } -+ -+ // get layer nswe -+ nsweo = block.getNswe(index); - } -+ { -+ // get block of the next cell -+ block = getBlock(ntx, nty); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nswet & dirt) == 0) -+ { -+ index = block.getIndexAbove(ntx, nty, gtz - GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ else -+ { -+ index = block.getIndexBelow(ntx, nty, gtz + GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ -+ // layer does not exist, return -+ if (index == -1) -+ { -+ return false; -+ } -+ -+ // get layer and next line of sight Z coordinate -+ gtz = block.getHeight(index); -+ lostz -= dz; -+ -+ // perform line of sight check, return when fails -+ if ((gtz - lostz) > Config.MAX_OBSTACLE_HEIGHT) -+ { -+ return false; -+ } -+ -+ // get layer nswe -+ nswet = block.getNswe(index); -+ } - -- prevX = curX; -- prevY = curY; -- prevGeoZ = curGeoZ; -- ++ptIndex; -+ // update coords -+ gox = nox; -+ goy = noy; -+ gtx = ntx; -+ gty = nty; - } - -- return true; -+ // when iteration is completed, compare final Z coordinates -+ return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); - } - - /** -- * Move check. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param zValue the z coordinate -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tzValue the target's z coordinate -- * @param instance the instance -- * @return the last Location (x,y,z) where player can walk - just before wall -+ * Check movement from coordinates to coordinates. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {code boolean} : True if target coordinates are reachable from origin coordinates - */ -- public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) -+ public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- final int geoX = getGeoX(x); -- final int geoY = getGeoY(y); -- final int z = getNearestZ(geoX, geoY, zValue); -- final int tGeoX = getGeoX(tx); -- final int tGeoY = getGeoY(ty); -- final int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } -- if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = z; -+ // perform geodata check -+ final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); -+ return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); -+ } -+ -+ /** -+ * Check movement from origin to target. Returns last available point in the checked path. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {@link Location} : Last point where object can walk (just before wall) -+ */ -+ public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) -+ { -+ return new Location(ox, oy, oz); -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) -+ { -+ return new Location(ox, oy, oz); -+ } - -- while (pointIter.next()) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -- -- if (hasGeoPos(prevX, prevY)) -- { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -- { -- // Can't move, return previous location. -- return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); -- } -- } -- -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return new Location(tx, ty, tz); - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != tz)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- // Different floors, return start location. -- return new Location(x, y, z); -+ return new Location(tx, ty, tz); - } - -- return new Location(tx, ty, tz); -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) -+ { -+ return new Location(tx, ty, tz); -+ } -+ -+ // perform geodata check -+ return checkMove(gox, goy, goz, gtx, gty, gtz, instance); - } - - /** -- * Checks if its possible to move from one location to another. -- * @param fromX the X coordinate to start checking from -- * @param fromY the Y coordinate to start checking from -- * @param fromZvalue the Z coordinate to start checking from -- * @param toX the X coordinate to end checking at -- * @param toY the Y coordinate to end checking at -- * @param toZvalue the Z coordinate to end checking at -- * @param instance the instance -- * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise -+ * With this method you can check if a position is visible or can be reached by beeline movement.
-+ * Target X and Y reachable and Z is on same floor: -+ *
    -+ *
  • Location of the target with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y reachable but Z is on another floor: -+ *
    -+ *
  • Location of the origin with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y not reachable: -+ *
    -+ *
  • Last accessible location in destination to target.
  • -+ *
-+ * @param gox : origin X geodata coordinate -+ * @param goy : origin Y geodata coordinate -+ * @param goz : origin Z geodata coordinate -+ * @param gtx : target X geodata coordinate -+ * @param gty : target Y geodata coordinate -+ * @param gtz : target Z geodata coordinate -+ * @param instance -+ * @return {@link GeoLocation} : The last allowed point of movement. - */ -- public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) -+ protected final GeoLocation checkMove(int gox, int goy, int goz, int gtx, int gty, int gtz, Instance instance) - { -- final int geoX = getGeoX(fromX); -- final int geoY = getGeoY(fromY); -- final int fromZ = getNearestZ(geoX, geoY, fromZvalue); -- final int tGeoX = getGeoX(toX); -- final int tGeoY = getGeoY(toY); -- final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); -- -- if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) -+ if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } -- if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) -+ if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = fromZ; -+ // get X delta, signum and direction flag -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirX = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; - -- while (pointIter.next()) -+ // get Y delta, signum and direction flag -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte dirY = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ -+ // get direction flag for diagonal movement -+ final byte dirXY = getDirXY(dirX, dirY); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte direction; -+ -+ // load pointer coordinates -+ int gpx = gox; -+ int gpy = goy; -+ int gpz = goz; -+ -+ // load next pointer -+ int nx = gpx; -+ int ny = gpy; -+ -+ // loop -+ int count = 0; -+ while (count++ < Config.MAX_ITERATIONS) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -+ direction = 0; - -- if (hasGeoPos(prevX, prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -+ d -= dy; -+ d += dx; -+ nx += sx; -+ ny += sy; -+ direction |= dirXY; -+ } -+ else if (e2 > -dy) -+ { -+ d -= dy; -+ nx += sx; -+ direction |= dirX; -+ } -+ else if (e2 < dx) -+ { -+ d += dx; -+ ny += sy; -+ direction |= dirY; -+ } -+ -+ // obstacle found, return -+ if ((getNsweNearest(gpx, gpy, gpz) & direction) == 0) -+ { -+ return new GeoLocation(gpx, gpy, gpz); -+ } -+ -+ // update pointer coordinates -+ gpx = nx; -+ gpy = ny; -+ gpz = getHeightNearest(nx, ny, gpz); -+ -+ // target coordinates reached -+ if ((gpx == gtx) && (gpy == gty)) -+ { -+ if (gpz == gtz) - { -- return false; -+ // path found, Z coordinates are okay, return target point -+ return new GeoLocation(gtx, gty, gtz); - } -+ -+ // path found, Z coordinates are not okay, return last good point -+ return new GeoLocation(gpx, gpy, gpz); - } -+ } -+ -+ return new GeoLocation(gox, goy, goz); -+ } -+ -+ /** -+ * Returns diagonal NSWE flag format of combined two NSWE flags. -+ * @param dirX : X direction NSWE flag -+ * @param dirY : Y direction NSWE flag -+ * @return byte : NSWE flag of combined direction -+ */ -+ private static byte getDirXY(byte dirX, byte dirY) -+ { -+ // check axis directions -+ if (dirY == GeoStructure.CELL_FLAG_N) -+ { -+ if (dirX == GeoStructure.CELL_FLAG_W) -+ { -+ return GeoStructure.CELL_FLAG_NW; -+ } - -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return GeoStructure.CELL_FLAG_NE; - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) -+ if (dirX == GeoStructure.CELL_FLAG_W) - { -- // Different floors. -- return false; -+ return GeoStructure.CELL_FLAG_SW; - } - -- return true; -+ return GeoStructure.CELL_FLAG_SE; - } - -+ /** -+ * Returns the list of location objects as a result of complete path calculation. -+ * @param ox : origin x -+ * @param oy : origin y -+ * @param oz : origin z -+ * @param tx : target x -+ * @param ty : target y -+ * @param tz : target z -+ * @param instance -+ * @return {@code List} : complete path from nodes -+ */ -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ return null; -+ } -+ -+ /** -+ * Returns the instance of the {@link GeoEngine}. -+ * @return {@link GeoEngine} : The instance. -+ */ - public static GeoEngine getInstance() - { - return SingletonHolder.INSTANCE; -@@ -743,6 +934,6 @@ - - private static class SingletonHolder - { -- protected static final GeoEngine INSTANCE = new GeoEngine(); -+ protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); - } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (working copy) -@@ -20,88 +20,84 @@ - import java.util.LinkedList; - import java.util.List; - import java.util.ListIterator; --import java.util.logging.Level; --import java.util.logging.Logger; - - import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; --import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; --import org.l2jmobius.gameserver.model.World; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -+import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.instancezone.Instance; - - /** -- * @author -Nemesiss- -+ * @author Hasha - */ --public class GeoEnginePathfinding -+final class GeoEnginePathfinding extends GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); -+ // pre-allocated buffers -+ private final BufferHolder[] _buffers; - -- private BufferInfo[] _buffers; -- - protected GeoEnginePathfinding() - { -- try -+ super(); -+ -+ final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ _buffers = new BufferHolder[array.length]; -+ int count = 0; -+ for (int i = 0; i < array.length; i++) - { -- final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ final String buf = array[i]; -+ final String[] args = buf.split("x"); - -- _buffers = new BufferInfo[array.length]; -- -- String buf; -- String[] args; -- for (int i = 0; i < array.length; i++) -+ try - { -- buf = array[i]; -- args = buf.split("x"); -- if (args.length != 2) -- { -- throw new Exception("Invalid buffer definition: " + buf); -- } -- -- _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); -+ final int size = Integer.parseInt(args[1]); -+ count += size; -+ _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); - } -+ catch (Exception e) -+ { -+ LOGGER.warning("GeoEnginePathfinding: Can not load buffer setting: " + buf); -+ } - } -- catch (Exception e) -- { -- LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); -- throw new Error("CellPathFinding: load aborted"); -- } -+ -+ LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); - } - -- public boolean pathNodesExist(short regionoffset) -+ @Override -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- return false; -- } -- -- public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) -- { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -- if (!GeoEngine.getInstance().hasGeo(x, y)) -+ // get origin and check existing geo coords -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { - 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)) -+ -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coords -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { - 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)))); -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // Prepare buffer for pathfinding calculations -+ final NodeBuffer buffer = getBuffer(64 + (2 * Math.max(Math.abs(gox - gtx), Math.abs(goy - gty)))); - if (buffer == null) - { - return null; - } - -- List path = null; -+ // find path -+ List path = null; - try - { -- final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); -- -+ final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); - if (result == null) - { - return null; -@@ -125,33 +121,45 @@ - return path; - } - -- int currentX, currentY, currentZ; -- ListIterator middlePoint; -+ // get path list iterator -+ final ListIterator point = path.listIterator(); - -- middlePoint = path.listIterator(); -- currentX = x; -- currentY = y; -- currentZ = z; -+ // get node A (origin) -+ int nodeAx = gox; -+ int nodeAy = goy; -+ short nodeAz = goz; - -- while (middlePoint.hasNext()) -+ // get node B -+ GeoLocation nodeB = (GeoLocation) point.next(); -+ -+ // iterate thought the path to optimize it -+ int count = 0; -+ while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) - { -- final AbstractNodeLoc locMiddle = middlePoint.next(); -- if (!middlePoint.hasNext()) -- { -- break; -- } -+ // get node C -+ final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - -- final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); -- if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) -+ // check movement from node A to node C -+ final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); -+ if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) - { -- middlePoint.remove(); -+ // can move from node A to node C -+ -+ // remove node B -+ point.remove(); - } - else - { -- currentX = locMiddle.getX(); -- currentY = locMiddle.getY(); -- currentZ = locMiddle.getZ(); -+ // can not move from node A to node C -+ -+ // set node A (node B is part of path, update A coordinates) -+ nodeAx = nodeB.getGeoX(); -+ nodeAy = nodeB.getGeoY(); -+ nodeAz = (short) nodeB.getZ(); - } -+ -+ // set node B -+ nodeB = (GeoLocation) point.next(); - } - - return path; -@@ -158,144 +166,102 @@ - } - - /** -- * Convert geodata position to pathnode position -- * @param geo_pos -- * @return pathnode position -+ * Create list of node locations as result of calculated buffer node tree. -+ * @param node : the entry point -+ * @return List : list of node location - */ -- public short getNodePos(int geo_pos) -+ private static List constructPath(Node node) - { -- return (short) (geo_pos >> 3); // OK? -- } -- -- /** -- * Convert node position to pathnode block position -- * @param node_pos -- * @return pathnode block position (0...255) -- */ -- public short getNodeBlock(int node_pos) -- { -- return (short) (node_pos % 256); -- } -- -- public byte getRegionX(int node_pos) -- { -- return (byte) ((node_pos >> 8) + World.TILE_X_MIN); -- } -- -- public byte getRegionY(int node_pos) -- { -- return (byte) ((node_pos >> 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 node_x rx -- * @return -- */ -- public int calculateWorldX(short node_x) -- { -- return World.MAP_MIN_X + (node_x * 128) + 48; -- } -- -- /** -- * Convert pathnode y to World y position -- * @param node_y -- * @return -- */ -- public int calculateWorldY(short node_y) -- { -- return World.MAP_MIN_Y + (node_y * 128) + 48; -- } -- -- private List constructPath(AbstractNode nodeValue) -- { -- final LinkedList path = new LinkedList<>(); -- int previousDirectionX = Integer.MIN_VALUE; -- int previousDirectionY = Integer.MIN_VALUE; -- int directionX, directionY; -+ // create empty list -+ final LinkedList list = new LinkedList<>(); - -- AbstractNode node = nodeValue; -- while (node.getParent() != null) -+ // set direction X/Y -+ int dx = 0; -+ int dy = 0; -+ -+ // get target parent -+ Node target = node; -+ Node parent = target.getParent(); -+ -+ // while parent exists -+ int count = 0; -+ while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { -- directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); -- directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); -+ // get parent <> target direction X/Y -+ final int nx = parent.getLoc().getGeoX() - target.getLoc().getGeoX(); -+ final int ny = parent.getLoc().getGeoY() - target.getLoc().getGeoY(); - -- // only add a new route point if moving direction changes -- if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) -+ // direction has changed? -+ if ((dx != nx) || (dy != ny)) - { -- previousDirectionX = directionX; -- previousDirectionY = directionY; -+ // add node to the beginning of the list -+ list.addFirst(target.getLoc()); - -- path.addFirst(node.getLoc()); -- node.setLoc(null); -+ // update direction X/Y -+ dx = nx; -+ dy = ny; - } - -- node = node.getParent(); -+ // move to next node, set target and get its parent -+ target = parent; -+ parent = target.getParent(); - } - -- return path; -+ // return list -+ return list; - } - -- private CellNodeBuffer alloc(int size) -+ /** -+ * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer. -+ * @param size : pre-calculated minimal required size -+ * @return NodeBuffer : buffer -+ */ -+ private final NodeBuffer getBuffer(int size) - { -- CellNodeBuffer current = null; -- for (BufferInfo i : _buffers) -+ NodeBuffer current = null; -+ for (BufferHolder holder : _buffers) - { -- if (i.mapSize >= size) -+ // Find proper size of buffer -+ if (holder._size < size) - { -- for (CellNodeBuffer buf : i.buffer) -+ continue; -+ } -+ -+ // Find unlocked NodeBuffer -+ for (NodeBuffer buffer : holder._buffer) -+ { -+ if (!buffer.isLocked()) - { -- if (buf.lock()) -- { -- current = buf; -- break; -- } -+ continue; - } -- if (current != null) -- { -- break; -- } - -- // not found, allocate temporary buffer -- current = new CellNodeBuffer(i.mapSize); -- current.lock(); -- if (i.buffer.size() < i.count) -- { -- i.buffer.add(current); -- break; -- } -+ return buffer; - } -+ -+ // NodeBuffer not found, allocate temporary buffer -+ current = new NodeBuffer(holder._size); -+ current.isLocked(); - } - - return current; - } - -- private static final class BufferInfo -+ /** -+ * NodeBuffer container with specified size and count of separate buffers. -+ */ -+ private static final class BufferHolder - { -- final int mapSize; -- final int count; -- ArrayList buffer; -+ final int _size; -+ List _buffer; - -- public BufferInfo(int size, int cnt) -+ public BufferHolder(int size, int count) - { -- mapSize = size; -- count = cnt; -- buffer = new ArrayList<>(count); -+ _size = size; -+ _buffer = new ArrayList<>(count); -+ for (int i = 0; i < count; i++) -+ { -+ _buffer.add(new NodeBuffer(size)); -+ } - } - } -- -- public static GeoEnginePathfinding getInstance() -- { -- return SingletonHolder.INSTANCE; -- } -- -- private static class SingletonHolder -- { -- protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); -- } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (working copy) -@@ -0,0 +1,197 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @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 height of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getHeightNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first above given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, above given coordinates. -+ */ -+ public abstract short getHeightAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first below given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is closest to given coordinates. -+ * @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 the NSWE flag byte of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getNsweNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first above given coordinates. -+ * @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 getNsweAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first below given coordinates. -+ * @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 getNsweBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is closes layer to given coordinates. -+ * @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. -+ * @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 above given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is first layer below given coordinates. -+ * @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 index to data of the cell, which is first layer below given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexBelowOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeight(int index); -+ -+ /** -+ * Returns the height of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightOriginal(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNswe(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNsweOriginal(int index); -+ -+ /** -+ * Sets the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @param nswe : New NSWE flag byte. -+ */ -+ public abstract void setNswe(int index, byte nswe); -+ -+ /** -+ * Saves the block in L2D format to {@link BufferedOutputStream}. Used only for L2D geodata conversion. -+ * @param stream : The stream. -+ * @throws IOException : Can't save the block to steam. -+ */ -+ public abstract void saveBlock(BufferedOutputStream stream) throws IOException; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (working copy) -@@ -0,0 +1,252 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockComplex(ByteBuffer bb, GeoFormat format) -+ { -+ // initialize buffer -+ _buffer = new byte[GeoStructure.BLOCK_CELLS * 3]; -+ -+ // load data -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // 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); -+ } -+ else -+ { -+ // get nswe -+ final byte nswe = bb.get(); -+ _buffer[i * 3] = nswe; -+ -+ // get height -+ final short height = bb.getShort(); -+ _buffer[(i * 3) + 1] = (byte) (height & 0x00FF); -+ _buffer[(i * 3) + 2] = (byte) (height >> 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height > worldZ ? height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height < worldZ ? height : Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public 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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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 int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_COMPLEX_L2D); -+ -+ // write block data -+ stream.write(_buffer, 0, GeoStructure.BLOCK_CELLS * 3); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (working copy) -@@ -0,0 +1,176 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockFlat extends ABlock -+{ -+ protected final short _height; -+ protected byte _nswe; -+ -+ /** -+ * Creates FlatBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockFlat(ByteBuffer bb, GeoFormat format) -+ { -+ _height = bb.getShort(); -+ _nswe = format != GeoFormat.L2D ? 0x0F : (byte) (0xFF); -+ if (format == GeoFormat.L2OFF) -+ { -+ bb.getShort(); -+ } -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return true; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height > worldZ ? _height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height < worldZ ? _height : Short.MAX_VALUE; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height > worldZ ? _nswe : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height < worldZ ? _nswe : 0; -+ } -+ -+ @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return index -+ return _height < worldZ ? 0 : -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _nswe = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_FLAT_L2D); -+ -+ // write height -+ stream.write((byte) (_height & 0x00FF)); -+ stream.write((byte) (_height >> 8)); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (working copy) -@@ -0,0 +1,465 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+import java.nio.ByteOrder; -+import java.util.Arrays; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockMultilayer extends ABlock -+{ -+ private static final int MAX_LAYERS = Byte.MAX_VALUE; -+ -+ private static ByteBuffer _temp; -+ -+ /** -+ * Initializes the temporarily buffer. -+ */ -+ public static void initialize() -+ { -+ // initialize temporarily buffer and sorting mechanism -+ _temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3); -+ _temp.order(ByteOrder.LITTLE_ENDIAN); -+ } -+ -+ /** -+ * Releases temporarily buffer. -+ */ -+ public static void release() -+ { -+ _temp = null; -+ } -+ -+ protected byte[] _buffer; -+ -+ /** -+ * Implicit constructor for children class. -+ */ -+ protected BlockMultilayer() -+ { -+ _buffer = null; -+ } -+ -+ /** -+ * Creates MultilayerBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockMultilayer(ByteBuffer bb, GeoFormat format) -+ { -+ // 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 = format != GeoFormat.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++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // get data -+ final short data = bb.getShort(); -+ -+ // add nswe and height -+ _temp.put((byte) (data & 0x000F)); -+ _temp.putShort((short) ((short) (data & 0xFFF0) >> 1)); -+ } -+ else -+ { -+ // add nswe -+ _temp.put(bb.get()); -+ -+ // add height -+ _temp.putShort(bb.getShort()); -+ } -+ } -+ } -+ -+ // 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 height -+ if (height > worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, return minimum value -+ return Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 height -+ if (height < worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, return maximum value -+ return Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 nswe -+ if (height > worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 nswe -+ if (height < worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @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 first layer data (first from bottom) -+ byte layers = _buffer[index++]; -+ -+ // loop though all cell layers, find closest layer -+ 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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ // get height -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(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]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ // get nswe -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ // set nswe -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_MULTILAYER_L2D); -+ -+ // for each cell -+ int index = 0; -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ // write layers count -+ final byte layers = _buffer[index++]; -+ stream.write(layers); -+ -+ // write cell data -+ stream.write(_buffer, index, layers * 3); -+ -+ // move index to next cell -+ index += layers * 3; -+ } -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (working copy) -@@ -0,0 +1,150 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockNull extends ABlock -+{ -+ private final byte _nswe; -+ -+ public BlockNull() -+ { -+ _nswe = (byte) 0xFF; -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return false; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweBelow(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) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) -+ { -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (nonexistent) -@@ -1,48 +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 . -- */ --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() -- { -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (nonexistent) -@@ -1,77 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (nonexistent) -@@ -1,56 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (working copy) -@@ -0,0 +1,39 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public enum GeoFormat -+{ -+ L2J("%d_%d.l2j"), -+ L2OFF("%d_%d_conv.dat"), -+ L2D("%d_%d.l2d"); -+ -+ private final String _filename; -+ -+ private GeoFormat(String filename) -+ { -+ _filename = filename; -+ } -+ -+ public String getFilename() -+ { -+ return _filename; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (working copy) -@@ -0,0 +1,67 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.geoengine.GeoEngine; -+import org.l2jmobius.gameserver.model.Location; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoLocation extends Location -+{ -+ private byte _nswe; -+ -+ public GeoLocation(int x, int y, int z) -+ { -+ super(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public void set(int x, int y, short z) -+ { -+ super.setXYZ(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public int getGeoX() -+ { -+ return _x; -+ } -+ -+ public int getGeoY() -+ { -+ return _y; -+ } -+ -+ @Override -+ public int getX() -+ { -+ return GeoEngine.getWorldX(_x); -+ } -+ -+ @Override -+ public int getY() -+ { -+ return GeoEngine.getWorldY(_y); -+ } -+ -+ public byte getNSWE() -+ { -+ return _nswe; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (working copy) -@@ -0,0 +1,70 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoStructure -+{ -+ // cells -+ public static final byte CELL_FLAG_E = 1 << 0; -+ public static final byte CELL_FLAG_W = 1 << 1; -+ public static final byte CELL_FLAG_S = 1 << 2; -+ public static final byte CELL_FLAG_N = 1 << 3; -+ public static final byte CELL_FLAG_SE = 1 << 4; -+ public static final byte CELL_FLAG_SW = 1 << 5; -+ public static final byte CELL_FLAG_NE = 1 << 6; -+ public static final byte CELL_FLAG_NW = (byte) (1 << 7); -+ -+ public static final int CELL_HEIGHT = 8; -+ public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6; -+ -+ // blocks -+ public static final byte TYPE_FLAT_L2J_L2OFF = 0; -+ public static final byte TYPE_FLAT_L2D = (byte) 0xD0; -+ public static final byte TYPE_COMPLEX_L2J = 1; -+ public static final byte TYPE_COMPLEX_L2OFF = 0x40; -+ public static final byte TYPE_COMPLEX_L2D = (byte) 0xD1; -+ 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) -+ public static final byte TYPE_MULTILAYER_L2D = (byte) 0xD2; -+ -+ 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; -+ -+ // regions -+ 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; -+ -+ // global geodata -+ private static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); -+ private 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; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (nonexistent) -@@ -1,42 +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 . -- */ --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); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (working copy) -@@ -0,0 +1,53 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public interface IGeoObject -+{ -+ /** -+ * Returns geodata X coordinate of the {@link IGeoObject}. -+ * @return int : Geodata X coordinate. -+ */ -+ int getGeoX(); -+ -+ /** -+ * Returns geodata Y coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Y coordinate. -+ */ -+ int getGeoY(); -+ -+ /** -+ * Returns geodata Z coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Z coordinate. -+ */ -+ int getGeoZ(); -+ -+ /** -+ * Returns height of the {@link IGeoObject}. -+ * @return int : Height. -+ */ -+ int getHeight(); -+ -+ /** -+ * Returns {@link IGeoObject} data. -+ * @return byte[][] : {@link IGeoObject} data. -+ */ -+ byte[][] getObjectGeoData(); -+} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (nonexistent) -@@ -1,47 +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 . -- */ --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(); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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); -- -- // 1 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (nonexistent) -@@ -1,55 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --/** -- * @author HorridoJoho -- */ --public final class NullRegion implements IRegion --{ -- public static final NullRegion INSTANCE = new NullRegion(); -- -- @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 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Region.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (nonexistent) -@@ -1,92 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (nonexistent) -@@ -1,87 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public abstract class AbstractNode --{ -- private T _loc; -- private AbstractNode _parent; -- -- public AbstractNode(T loc) -- { -- _loc = loc; -- } -- -- public void setParent(AbstractNode p) -- { -- _parent = p; -- } -- -- public AbstractNode getParent() -- { -- return _parent; -- } -- -- public T getLoc() -- { -- return _loc; -- } -- -- public void setLoc(T l) -- { -- _loc = l; -- } -- -- @Override -- public int hashCode() -- { -- final int prime = 31; -- int result = 1; -- result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); -- return result; -- } -- -- @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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (nonexistent) -@@ -1,33 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --/** -- * @author -Nemesiss- -- */ --public abstract class AbstractNodeLoc --{ -- public abstract int getX(); -- -- public abstract int getY(); -- -- public abstract int getZ(); -- -- public abstract int getNodeX(); -- -- public abstract int getNodeY(); --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (nonexistent) -@@ -1,67 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public class CellNode extends AbstractNode --{ -- 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (nonexistent) -@@ -1,348 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --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 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) -- { -- _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(); -- } -- -- public final List debugPath() -- { -- final List 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 void getNeighbors() -- { -- if (!_current.getLoc().canGoAll()) -- { -- 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); -- } -- -- // SouthEast -- if ((nodeE != null) && (nodeS != null)) -- { -- if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) -- { -- addNode(x + 1, y + 1, z, true); -- } -- } -- -- // SouthWest -- if ((nodeS != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) -- { -- addNode(x - 1, y + 1, z, true); -- } -- } -- -- // NorthEast -- if ((nodeN != null) && (nodeE != null)) -- { -- if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) -- { -- addNode(x + 1, y - 1, z, true); -- } -- } -- -- // NorthWest -- if ((nodeN != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) -- { -- addNode(x - 1, y - 1, z, true); -- } -- } -- } -- -- private 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(); -- // reinit node if needed -- if (result.getLoc() != null) -- { -- result.getLoc().set(x, y, z); -- } -- else -- { -- result.setLoc(new NodeLoc(x, y, z)); -- } -- } -- -- return result; -- } -- -- private 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 boolean isHighWeight(int x, int y, int z) -- { -- final CellNode result = getNode(x, y, z); -- if (result == null) -- { -- return true; -- } -- -- if (!result.getLoc().canGoAll()) -- { -- return true; -- } -- if (Math.abs(result.getLoc().getZ() - z) > 16) -- { -- return true; -- } -- -- return false; -- } -- -- private 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (working copy) -@@ -0,0 +1,87 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+ -+/** -+ * @author Hasha -+ */ -+public class Node -+{ -+ // node coords and nswe flag -+ private GeoLocation _loc; -+ -+ // node parent (for reverse path construction) -+ private Node _parent; -+ // node child (for moving over nodes during iteration) -+ private Node _child; -+ -+ // node G cost (movement cost = parent movement cost + current movement cost) -+ private double _cost = -1000; -+ -+ public void setLoc(int x, int y, int z) -+ { -+ _loc = new GeoLocation(x, y, z); -+ } -+ -+ public GeoLocation getLoc() -+ { -+ return _loc; -+ } -+ -+ public void setParent(Node parent) -+ { -+ _parent = parent; -+ } -+ -+ public Node getParent() -+ { -+ return _parent; -+ } -+ -+ public void setChild(Node child) -+ { -+ _child = child; -+ } -+ -+ public Node getChild() -+ { -+ return _child; -+ } -+ -+ public void setCost(double cost) -+ { -+ _cost = cost; -+ } -+ -+ public double getCost() -+ { -+ return _cost; -+ } -+ -+ public void free() -+ { -+ // reset node location -+ _loc = null; -+ -+ // reset node parent, child and cost -+ _parent = null; -+ _child = null; -+ _cost = -1000; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (working copy) -@@ -0,0 +1,306 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import java.util.concurrent.locks.ReentrantLock; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+ -+/** -+ * @author DS, Hasha; Credits to Diamond -+ */ -+public class NodeBuffer -+{ -+ private final ReentrantLock _lock = new ReentrantLock(); -+ private final int _size; -+ private final Node[][] _buffer; -+ -+ // center coordinates -+ private int _cx = 0; -+ private int _cy = 0; -+ -+ // target coordinates -+ private int _gtx = 0; -+ private int _gty = 0; -+ private short _gtz = 0; -+ -+ private Node _current = null; -+ -+ /** -+ * Constructor of NodeBuffer. -+ * @param size : one dimension size of buffer -+ */ -+ public NodeBuffer(int size) -+ { -+ // set size -+ _size = size; -+ -+ // initialize buffer -+ _buffer = new Node[size][size]; -+ for (int x = 0; x < size; x++) -+ { -+ for (int y = 0; y < size; y++) -+ { -+ _buffer[x][y] = 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 Node : first node of path -+ */ -+ public Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) -+ { -+ // set coordinates (middle of the line (gox,goy) - (gtx,gty), will be in the center of the buffer) -+ _cx = gox + ((gtx - gox - _size) / 2); -+ _cy = goy + ((gty - goy - _size) / 2); -+ -+ _gtx = gtx; -+ _gty = gty; -+ _gtz = gtz; -+ -+ _current = getNode(gox, goy, goz); -+ _current.setCost(getCostH(gox, goy, goz)); -+ -+ int count = 0; -+ do -+ { -+ // reached target? -+ if ((_current.getLoc().getGeoX() == _gtx) && (_current.getLoc().getGeoY() == _gty) && (Math.abs(_current.getLoc().getZ() - _gtz) < 8)) -+ { -+ return _current; -+ } -+ -+ // expand current node -+ expand(); -+ -+ // move pointer -+ _current = _current.getChild(); -+ } -+ while ((_current != null) && (count++ < Config.MAX_ITERATIONS)); -+ -+ return null; -+ } -+ -+ public boolean isLocked() -+ { -+ return _lock.tryLock(); -+ } -+ -+ public void free() -+ { -+ _current = null; -+ -+ for (Node[] nodes : _buffer) -+ { -+ for (Node node : nodes) -+ { -+ if (node.getLoc() != null) -+ { -+ node.free(); -+ } -+ } -+ } -+ -+ _lock.unlock(); -+ } -+ -+ /** -+ * Check _current Node and add its neighbors to the buffer. -+ */ -+ private final void expand() -+ { -+ // can't move anywhere, don't expand -+ final byte nswe = _current.getLoc().getNSWE(); -+ if (nswe == 0) -+ { -+ return; -+ } -+ -+ // get geo coords of the node to be expanded -+ final int x = _current.getLoc().getGeoX(); -+ final int y = _current.getLoc().getGeoY(); -+ final short z = (short) _current.getLoc().getZ(); -+ -+ // can move north, expand -+ if ((nswe & GeoStructure.CELL_FLAG_N) != 0) -+ { -+ addNode(x, y - 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move south, expand -+ if ((nswe & GeoStructure.CELL_FLAG_S) != 0) -+ { -+ addNode(x, y + 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_W) != 0) -+ { -+ addNode(x - 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_E) != 0) -+ { -+ addNode(x + 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move north-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NW) != 0) -+ { -+ addNode(x - 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move north-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NE) != 0) -+ { -+ addNode(x + 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SW) != 0) -+ { -+ addNode(x - 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SE) != 0) -+ { -+ addNode(x + 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ } -+ -+ /** -+ * Returns node, if it exists in buffer. -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param z : node Z coord -+ * @return Node : node, if exits in buffer -+ */ -+ private final Node getNode(int x, int y, short z) -+ { -+ // check node X out of coordinates -+ final int ix = x - _cx; -+ if ((ix < 0) || (ix >= _size)) -+ { -+ return null; -+ } -+ -+ // check node Y out of coordinates -+ final int iy = y - _cy; -+ if ((iy < 0) || (iy >= _size)) -+ { -+ return null; -+ } -+ -+ // get node -+ final Node result = _buffer[ix][iy]; -+ -+ // check and update -+ if (result.getLoc() == null) -+ { -+ result.setLoc(x, y, z); -+ } -+ -+ // return node -+ return result; -+ } -+ -+ /** -+ * Add node given by coordinates to the buffer. -+ * @param x : geo X coord -+ * @param y : geo Y coord -+ * @param z : geo Z coord -+ * @param weight : weight of movement to new node -+ */ -+ private final void addNode(int x, int y, short z, int weight) -+ { -+ // get node to be expanded -+ final Node node = getNode(x, y, z); -+ if (node == null) -+ { -+ return; -+ } -+ -+ // Z distance between nearby cells is higher than cell size -+ if (node.getLoc().getZ() > (z + (2 * GeoStructure.CELL_HEIGHT))) -+ { -+ return; -+ } -+ -+ // node was already expanded, return -+ if (node.getCost() >= 0) -+ { -+ return; -+ } -+ -+ node.setParent(_current); -+ if (node.getLoc().getNSWE() != (byte) 0xFF) -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + (weight * Config.OBSTACLE_MULTIPLIER)); -+ } -+ else -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + weight); -+ } -+ -+ Node current = _current; -+ int count = 0; -+ while ((current.getChild() != null) && (count < (Config.MAX_ITERATIONS * 4))) -+ { -+ count++; -+ if (current.getChild().getCost() > node.getCost()) -+ { -+ node.setChild(current.getChild()); -+ break; -+ } -+ current = current.getChild(); -+ } -+ -+ if (count >= (Config.MAX_ITERATIONS * 4)) -+ { -+ System.err.println("Pathfinding: too long loop detected, cost:" + node.getCost()); -+ } -+ -+ current.setChild(node); -+ } -+ -+ /** -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param i : node Z coord -+ * @return double : node cost -+ */ -+ private final double getCostH(int x, int y, int i) -+ { -+ final int dX = x - _gtx; -+ final int dY = y - _gty; -+ final int dZ = (i - _gtz) / GeoStructure.CELL_HEIGHT; -+ -+ // return (Math.abs(dX) + Math.abs(dY) + Math.abs(dZ)) * Config.HEURISTIC_WEIGHT; // Manhattan distance -+ return Math.sqrt((dX * dX) + (dY * dY) + (dZ * dZ)) * Config.HEURISTIC_WEIGHT; // Direct distance -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -- --/** -- * @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 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 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; -- // return super.hashCode(); -- } -- -- @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; -- if (_x != other._x) -- { -- return false; -- } -- if (_y != other._y) -- { -- return false; -- } -- if (_goNorth != other._goNorth) -- { -- return false; -- } -- if (_goEast != other._goEast) -- { -- return false; -- } -- if (_goSouth != other._goSouth) -- { -- return false; -- } -- if (_goWest != other._goWest) -- { -- return false; -- } -- if (_geoHeight != other._geoHeight) -- { -- return false; -- } -- return true; -- } --} -Index: java/org/l2jmobius/gameserver/model/actor/Creature.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/Creature.java (revision 8428) -+++ java/org/l2jmobius/gameserver/model/actor/Creature.java (working copy) -@@ -65,8 +65,6 @@ - import org.l2jmobius.gameserver.enums.TeleportWhereType; - import org.l2jmobius.gameserver.enums.UserInfoType; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; - import org.l2jmobius.gameserver.instancemanager.IdManager; - import org.l2jmobius.gameserver.instancemanager.MapRegionManager; - import org.l2jmobius.gameserver.instancemanager.QuestManager; -@@ -2566,7 +2564,7 @@ - - public boolean disregardingGeodata; - public int onGeodataPathIndex; -- public List geoPath; -+ public List geoPath; - public int geoPathAccurateTx; - public int geoPathAccurateTy; - public int geoPathGtx; -@@ -3443,7 +3441,7 @@ - if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) - { - // Path calculation -- overrides previous movement check -- m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); -+ m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); - if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found - { - if (isPlayer() && !_isFlying && !isInWater) -Index: java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (revision 8424) -+++ java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (working copy) -@@ -12746,7 +12746,7 @@ - { - if (Config.CORRECT_PLAYER_Z) - { -- final int nearestZ = GeoEngine.getInstance().getHigherHeight(getX(), getY(), getZ()); -+ final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); - if (getZ() < nearestZ) - { - teleToLocation(new Location(getX(), getY(), nearestZ)); -Index: java/org/l2jmobius/gameserver/util/GeoUtils.java -=================================================================== ---- java/org/l2jmobius/gameserver/util/GeoUtils.java (revision 8409) -+++ java/org/l2jmobius/gameserver/util/GeoUtils.java (working copy) -@@ -19,7 +19,7 @@ - import java.awt.Color; - - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; - import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; - -@@ -26,25 +26,25 @@ - /** - * @author HorridoJoho - */ --public final class GeoUtils -+public class GeoUtils - { - public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); - - final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); - - while (iter.next()) - { -- final int wx = GeoEngine.getInstance().getWorldX(iter.x()); -- final int wy = GeoEngine.getInstance().getWorldY(iter.y()); -+ final int wx = GeoEngine.getWorldX(iter.x()); -+ final int wy = GeoEngine.getWorldY(iter.y()); - - prim.addPoint(Color.RED, wx, wy, z); - } -@@ -53,21 +53,21 @@ - - public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); -- int wy = GeoEngine.getInstance().getWorldY(prevY); -+ int wx = GeoEngine.getWorldX(prevX); -+ int wy = GeoEngine.getWorldY(prevY); - int wz = iter.z(); - prim.addPoint(Color.RED, wx, wy, wz); - -@@ -78,8 +78,8 @@ - - if ((curX != prevX) || (curY != prevY)) - { -- wx = GeoEngine.getInstance().getWorldX(curX); -- wy = GeoEngine.getInstance().getWorldY(curY); -+ wx = GeoEngine.getWorldX(curX); -+ wy = GeoEngine.getWorldY(curY); - wz = iter.z(); - - prim.addPoint(Color.RED, wx, wy, wz); -@@ -93,7 +93,7 @@ - - private static Color getDirectionColor(int x, int y, int z, int nswe) - { -- if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) -+ if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) - { - return Color.GREEN; - } -@@ -109,9 +109,8 @@ - int iPacket = 0; - - ExServerPrimitive exsp = null; -- final GeoEngine ge = GeoEngine.getInstance(); -- final int playerGx = ge.getGeoX(player.getX()); -- final int playerGy = ge.getGeoY(player.getY()); -+ final int playerGx = GeoEngine.getGeoX(player.getX()); -+ final int playerGy = GeoEngine.getGeoY(player.getY()); - for (int dx = -geoRadius; dx <= geoRadius; ++dx) - { - for (int dy = -geoRadius; dy <= geoRadius; ++dy) -@@ -135,12 +134,12 @@ - final int gx = playerGx + dx; - final int gy = playerGy + dy; - -- final int x = ge.getWorldX(gx); -- final int y = ge.getWorldY(gy); -- final int z = ge.getNearestZ(gx, gy, player.getZ()); -+ final int x = GeoEngine.getWorldX(gx); -+ final int y = GeoEngine.getWorldY(gy); -+ final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); - - // north arrow -- Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); -+ Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); - 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); -@@ -147,7 +146,7 @@ - exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z); - - // east arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); - 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); -@@ -154,13 +153,13 @@ - exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z); - - // south arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); - 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, Cell.NSWE_WEST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); - 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); -@@ -188,15 +187,15 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_EAST; -+ return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_EAST; -+ return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; - } - else - { -- return Cell.NSWE_EAST; -+ return GeoStructure.CELL_FLAG_E; // Direction.EAST; - } - } - else if (x < lastX) // west -@@ -203,26 +202,27 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_WEST; -+ return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_WEST; -+ return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; - } - else - { -- return Cell.NSWE_WEST; -+ return GeoStructure.CELL_FLAG_W; // Direction.WEST; - } - } -- else // unchanged x -+ else -+ // unchanged x - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH; -+ return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH; -+ return GeoStructure.CELL_FLAG_N; // Direction.NORTH; - } - else - { -Index: java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java -=================================================================== ---- java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (nonexistent) -+++ java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (working copy) -@@ -0,0 +1,386 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.tools.geodataconverter; -+ -+import java.io.BufferedOutputStream; -+import java.io.File; -+import java.io.FileOutputStream; -+import java.io.RandomAccessFile; -+import java.nio.ByteOrder; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.Scanner; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.commons.util.PropertiesParser; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoDataConverter -+{ -+ private static GeoFormat _format; -+ private static ABlock[][] _blocks; -+ -+ public static void main(String[] args) -+ { -+ // initialize config -+ loadGeoengineConfigs(); -+ -+ // get geodata format -+ String type = ""; -+ try (Scanner scn = new Scanner(System.in)) -+ { -+ while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) -+ { -+ System.out.println("GeoDataConverter: Select source geodata type:"); -+ System.out.println(" J: L2J (e.g. 23_22.l2j)"); -+ System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); -+ System.out.println(" E: Exit"); -+ System.out.print("Choice: "); -+ type = scn.next(); -+ } -+ } -+ if (type.equalsIgnoreCase("E")) -+ { -+ System.exit(0); -+ } -+ -+ _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; -+ -+ // start conversion -+ System.out.println("GeoDataConverter: Converting all " + _format + " files."); -+ -+ // initialize geodata container -+ _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files -+ int converted = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) -+ { -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) -+ { -+ final String input = String.format(_format.getFilename(), rx, ry); -+ final String filepath = Config.GEODATA_PATH; -+ final File f = new File(filepath + input); -+ if (f.exists() && !f.isDirectory()) -+ { -+ // load geodata -+ if (!loadGeoBlocks(input)) -+ { -+ System.out.println("GeoDataConverter: Unable to load " + input + " region file."); -+ continue; -+ } -+ -+ // recalculate nswe -+ if (!recalculateNswe()) -+ { -+ System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); -+ continue; -+ } -+ -+ // save geodata -+ final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); -+ if (!saveGeoBlocks(output)) -+ { -+ System.out.println("GeoDataConverter: Unable to save " + output + " region file."); -+ continue; -+ } -+ -+ converted++; -+ System.out.println("GeoDataConverter: Created " + output + " region file."); -+ } -+ } -+ } -+ System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); -+ } -+ -+ /** -+ * Loads geo blocks from buffer of the region file. -+ * @param filename : The name of the to load. -+ * @return boolean : True when successful. -+ */ -+ private static boolean loadGeoBlocks(String filename) -+ { -+ // region file is load-able, try to load it -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) -+ { -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) -+ if (_format == GeoFormat.L2OFF) -+ { -+ for (int i = 0; i < 18; i++) -+ { -+ buffer.get(); -+ } -+ } -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ if (_format == GeoFormat.L2J) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2J: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2J: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ else -+ { -+ // get block type -+ final short type = buffer.getShort(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ default: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ if (buffer.remaining() > 0) -+ { -+ System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ return false; -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); -+ return false; -+ } -+ } -+ -+ /** -+ * Recalculate diagonal flags for the region file. -+ * @return boolean : True when successful. -+ */ -+ private static boolean recalculateNswe() -+ { -+ try -+ { -+ for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) -+ { -+ for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) -+ { -+ // get block -+ final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // skip flat blocks -+ if (block instanceof BlockFlat) -+ { -+ continue; -+ } -+ -+ // for complex and multilayer blocks go though all layers -+ short height = Short.MAX_VALUE; -+ int index; -+ while ((index = block.getIndexBelow(x, y, height)) != -1) -+ { -+ // get height and nswe -+ height = block.getHeight(index); -+ byte nswe = block.getNswe(index); -+ -+ // update nswe with diagonal flags -+ nswe = updateNsweBelow(x, y, height, nswe); -+ -+ // set nswe of the cell -+ block.setNswe(index, nswe); -+ } -+ } -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ /** -+ * Updates the NSWE flag with diagonal flags. -+ * @param x : Geodata X coordinate. -+ * @param y : Geodata Y coordinate. -+ * @param z : Geodata Z coordinate. -+ * @param nsweValue : NSWE flag to be updated. -+ * @return byte : Updated NSWE flag. -+ */ -+ private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) -+ { -+ byte nswe = nsweValue; -+ -+ // calculate virtual layer height -+ final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); -+ -+ // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) -+ final byte nsweN = getNsweBelow(x, y - 1, height); -+ final byte nsweS = getNsweBelow(x, y + 1, height); -+ final byte nsweW = getNsweBelow(x - 1, y, height); -+ final byte nsweE = getNsweBelow(x + 1, y, height); -+ -+ // north-west -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NW; -+ } -+ -+ // north-east -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NE; -+ } -+ -+ // south-west -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SW; -+ } -+ -+ // south-east -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SE; -+ } -+ -+ return nswe; -+ } -+ -+ private static byte getNsweBelow(int geoX, int geoY, short worldZ) -+ { -+ // out of geo coordinates -+ if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) -+ { -+ return 0; -+ } -+ -+ // out of geo coordinates -+ if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) -+ { -+ return 0; -+ } -+ -+ // get block -+ final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // get index, when valid, return nswe -+ final int index = block.getIndexBelow(geoX, geoY, worldZ); -+ return index == -1 ? 0 : block.getNswe(index); -+ } -+ -+ /** -+ * Save region file to file. -+ * @param filename : The name of file to save. -+ * @return boolean : True when successful. -+ */ -+ private static boolean saveGeoBlocks(String filename) -+ { -+ try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) -+ { -+ // loop over region blocks and save each block -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[ix][iy].saveBlock(bos); -+ } -+ } -+ -+ // flush data to file -+ bos.flush(); -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ private static void loadGeoengineConfigs() -+ { -+ final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); -+ Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); -+ Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); -+ Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); -+ Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); -+ Config.PATHFINDING = geoData.getBoolean("PathFinding", true); -+ Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -+ Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); -+ Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); -+ Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); -+ Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); -+ Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); -+ } -+} -\ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/geodata/Readme.txt b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/geodata/Readme.txt index a480c8c380..2611882e56 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/geodata/Readme.txt +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/geodata/Readme.txt @@ -23,8 +23,8 @@ a - Prerequisites b - Make it work ---------------------------------------------- -To make geodata working: -* unpack your geodata files into "/data/geodata" folder -* open "/config/GeoEngine.ini" with your favorite text editor and then edit following config: - - CoordSynchronize = 2 -* If you do not use any geodata files, the server will automatically change this setting to -1. +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. diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java index 037b29d579..89425b0d85 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java @@ -295,7 +295,7 @@ public class FourSepulchers extends AbstractNpcAI implements IXmlReader { if ((npc != null) && !npc.isDead()) { - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), npc.getSpawn().getLocation().getX() + getRandom(-400, 400), npc.getSpawn().getLocation().getY() + getRandom(-400, 400), npc.getZ(), npc.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), npc.getSpawn().getLocation().getX() + getRandom(-400, 400), npc.getSpawn().getLocation().getY() + getRandom(-400, 400), npc.getZ(), npc.getInstanceWorld()); if (Util.calculateDistance(npc, npc.getSpawn().getLocation(), false, false) < 600) { npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java index 7aa8789df0..00bffa39b8 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java @@ -270,7 +270,7 @@ public class PrimevalIsle extends AbstractNpcAI final double cos = Math.cos(radian); final int newX = (int) (npc.getX() + (cos * distance)); final int newY = (int) (npc.getY() + (sin * distance)); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, npc.getZ(), npc.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, npc.getZ(), npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, loc, 0); } else if (ag_type == 1) diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java index 0a77f7404d..772f5fb5d7 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java @@ -78,7 +78,7 @@ public class BoyAndGirl extends AbstractNpcAI startQuestTimer("NPC_SHOUT", 10000 + (getRandom(5) * 1000), npc, null); npc.setRunning(); final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 200, 600); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); return super.onSpawn(npc); } @@ -86,7 +86,7 @@ public class BoyAndGirl extends AbstractNpcAI public void onMoveFinished(Npc npc) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 200, 600); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); super.onMoveFinished(npc); } diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java index 558f2631e6..3bcf3810cb 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java @@ -48,7 +48,7 @@ public class Devno extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java index 7525cc99e8..ce81f5c05c 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java @@ -48,7 +48,7 @@ public class Eleve extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java index 7d9150b156..f4e942c326 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java @@ -46,7 +46,7 @@ public class Handermonkey extends AbstractNpcAI { final int x = npc.getSpawn().getX() + (getRandom(-100, 100)); final int y = npc.getSpawn().getY() + (getRandom(-100, 100)); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x, y, npc.getZ(), npc.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x, y, npc.getZ(), npc.getInstanceWorld()); addMoveToDesire(npc, loc, 0); } else diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java index c993ce8936..7df61da041 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java @@ -48,7 +48,7 @@ public class Karonf extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java index 6e8cbee920..589d376f6b 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java @@ -48,7 +48,7 @@ public class Marsha extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java index c616abccc6..5c09d531c8 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java @@ -48,7 +48,7 @@ public class Morgan extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java index 3652b4ab02..fe20952121 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java @@ -48,7 +48,7 @@ public class Rubentis extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", 10000 + (getRandom(5) * 1000), npc, null); } diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java index beece64521..b0944f763d 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java @@ -63,7 +63,7 @@ public class Vortex extends AbstractNpcAI final int x = (int) (attackers.getX() + (600 * Math.cos(radians))); final int y = (int) (attackers.getY() + (600 * Math.sin(radians))); final int z = attackers.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(attackers.getX(), attackers.getY(), attackers.getZ(), x, y, z, attackers.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(attackers.getX(), attackers.getY(), attackers.getZ(), x, y, z, attackers.getInstanceWorld()); attackers.broadcastPacket(new FlyToLocation(attackers, x, y, z, FlyToLocation.FlyType.THROW_UP, 800, 800, 800)); attackers.setXYZ(loc); attackers.broadcastPacket(new ValidateLocation(attackers)); diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/others/FleeMonsters.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/others/FleeMonsters.java index 9346aebc95..b5de804b28 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/others/FleeMonsters.java +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/others/FleeMonsters.java @@ -68,7 +68,7 @@ public class FleeMonsters extends AbstractNpcAI final int posX = (int) (npc.getX() + (FLEE_DISTANCE * Math.cos(radians))); final int posY = (int) (npc.getY() + (FLEE_DISTANCE * Math.sin(radians))); final int posZ = npc.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, attacker.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); return super.onAttack(npc, attacker, damage, isSummon); } diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index 524652cbc1..18511c6541 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { @@ -133,8 +133,8 @@ public class AdminGeodata implements IAdminCommandHandler } case "admin_geomap": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y + " (" + ((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + "," + ((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + " to " + ((((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + "," + ((((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + ")"); break; } diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java index f3dffa1227..76572f7f25 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java @@ -57,8 +57,8 @@ public class AdminMissingHtmls implements IAdminCommandHandler { case "admin_geomap_missing_htmls": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final int topLeftX = (x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE; final int topLeftY = (y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE; final int bottomRightX = (((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1; diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index fdb2319138..a0a34f1051 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -19,9 +19,9 @@ package handlers.admincommandhandlers; import java.util.List; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; +import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.util.BuilderUtil; @@ -44,13 +44,13 @@ public class AdminPathNode implements IAdminCommandHandler } if (activeChar.getTarget() != null) { - final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { BuilderUtil.sendSysMessage(activeChar, "No Route!"); return true; } - for (AbstractNodeLoc a : path) + for (Location a : path) { BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/Blink.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/Blink.java index 22f2c84eae..22697e8e20 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/Blink.java +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/Blink.java @@ -88,7 +88,7 @@ public class Blink extends AbstractEffect final int y = effected.getY() + y1; final int z = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, destination, _flyType, _flySpeed, _flyDelay, _animationSpeed)); diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/Fear.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/Fear.java index 95a5afdf98..88c795e04b 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/Fear.java +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/Fear.java @@ -100,7 +100,7 @@ public class Fear extends AbstractEffect final int posY = (int) (effected.getY() + (FEAR_RANGE * Math.sin(radians))); final int posZ = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); } } diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java index fddd94c60a..55c2e78d8a 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java @@ -57,7 +57,7 @@ public class FlyAway extends AbstractEffect final int y = (int) (effector.getY() - (nRadius * (dy / distance))); final int z = effector.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.broadcastPacket(new FlyToLocation(effected, destination, FlyType.THROW_UP)); effected.setXYZ(destination); diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java index 3c3f9aca62..14809ac499 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java @@ -179,7 +179,7 @@ public class KnockBack extends AbstractEffect final int x = (int) (effected.getX() + (_distance * Math.cos(radians))); final int y = (int) (effected.getY() + (_distance * Math.sin(radians))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, loc, _type, _speed, _delay, _animationSpeed)); diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/PullBack.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/PullBack.java index ed6a37eff6..38fe2fe9d0 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/PullBack.java +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/PullBack.java @@ -73,7 +73,7 @@ public class PullBack extends AbstractEffect } // In retail, you get debuff, but you are not even moved if there is obstacle. You are still disabled from using skills and moving though. - if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effector.getInstanceWorld())) + if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effected.getInstanceWorld())) { effected.broadcastPacket(new FlyToLocation(effected, effector, _type, _speed, _delay, _animationSpeed)); effected.setXYZ(effector.getX(), effector.getY(), GeoEngine.getInstance().getHeight(effector.getX(), effector.getY(), effector.getZ()) + 10); diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java index d6f9664141..813e496c47 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java @@ -87,7 +87,7 @@ public class TeleportToSummon extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = summon.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java index d3c263978c..c9dc34ac22 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java @@ -76,7 +76,7 @@ public class TeleportToTarget extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java index f2148adb54..c7ca6f64cb 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java @@ -65,7 +65,7 @@ public class RollingDice implements IItemHandler final int x = player.getX() + x1; final int y = player.getY() + y1; final int z = player.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); Broadcast.toSelfAndKnownPlayers(player, new Dice(player.getObjectId(), itemId, number, destination.getX(), destination.getY(), destination.getZ())); final SystemMessage sm = new SystemMessage(SystemMessageId.C1_HAS_ROLLED_A_S2); diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/targethandlers/Ground.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/targethandlers/Ground.java index acce8f1b2f..1328ff01b5 100644 --- a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/targethandlers/Ground.java +++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/handlers/targethandlers/Ground.java @@ -52,7 +52,7 @@ public class Ground implements ITargetTypeHandler return null; } - if (!GeoEngine.getInstance().canSeeTarget(creature, worldPosition)) + if (!GeoEngine.getInstance().canSeeLocation(creature, worldPosition)) { if (sendMessage) { diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/Config.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/Config.java index e4678dc60a..7700553d55 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/Config.java @@ -61,6 +61,7 @@ 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; @@ -942,12 +943,17 @@ public class Config // GeoEngine // -------------------------------------------------- public static Path GEODATA_PATH; + public static GeoType GEODATA_TYPE; public static boolean PATHFINDING; public static String PATHFIND_BUFFERS; - public static float LOW_WEIGHT; - public static float MEDIUM_WEIGHT; - public static float HIGH_WEIGHT; - public static float DIAGONAL_WEIGHT; + public static int MOVE_WEIGHT; + public static int MOVE_WEIGHT_DIAG; + public static int OBSTACLE_WEIGHT; + 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; /** Attribute System */ public static int S_WEAPON_STONE; @@ -2488,12 +2494,17 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); + GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); - MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); - HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); - DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); + MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); + OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); + HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); + MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); + MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); // Load AllowedPlayerRaces config file (if exists) final PropertiesParser AllowedPlayerRaces = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE); diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/commons/util/CommonUtil.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/commons/util/CommonUtil.java index 14ae3a4560..ab7837d827 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/commons/util/CommonUtil.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/commons/util/CommonUtil.java @@ -584,4 +584,15 @@ public class CommonUtil final DecimalFormat formatter = new DecimalFormat(format, new DecimalFormatSymbols(Locale.ENGLISH)); return formatter.format(value); } + + /** + * @param numToTest : The number to test. + * @param min : The minimum limit. + * @param max : The maximum limit. + * @return the number or one of the limit (mininum / maximum). + */ + public static int limit(int numToTest, int min, int max) + { + return (numToTest > max) ? max : ((numToTest < min) ? min : numToTest); + } } diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/commons/util/Point2D.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/commons/util/Point2D.java new file mode 100644 index 0000000000..ebb6272e5e --- /dev/null +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/commons/util/Point2D.java @@ -0,0 +1,180 @@ +/* + * 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 . + */ +package org.l2jmobius.commons.util; + +import java.util.Objects; + +/** + * A datatype used to retain a 2D (x/y) point. It got the capability to be set and cleaned. + */ +public class Point2D +{ + protected volatile int _x; + protected volatile int _y; + + public Point2D(int x, int y) + { + _x = x; + _y = y; + } + + @Override + public Point2D clone() + { + return new Point2D(_x, _y); + } + + @Override + public String toString() + { + return _x + ", " + _y; + } + + @Override + public int hashCode() + { + return Objects.hash(_x, _y); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final Point2D other = (Point2D) obj; + return (_x == other._x) && (_y == other._y); + } + + /** + * @param x : The X coord to test. + * @param y : The Y coord to test. + * @return True if all coordinates equals this {@link Point2D} coordinates. + */ + public boolean equals(int x, int y) + { + return (_x == x) && (_y == y); + } + + public int getX() + { + return _x; + } + + public void setX(int x) + { + _x = x; + } + + public int getY() + { + return _y; + } + + public void setY(int y) + { + _y = y; + } + + public void set(int x, int y) + { + _x = x; + _y = y; + } + + /** + * Refresh the current {@link Point2D} using a reference {@link Point2D} and a distance. The new destination is calculated to go in opposite side of the {@link Point2D} reference.
+ *
+ * This method is perfect to calculate fleeing characters position. + * @param referenceLoc : The Point2D used as position. + * @param distance : The distance to be set between current and new position. + */ + public void setFleeing(Point2D referenceLoc, int distance) + { + final double xDiff = referenceLoc.getX() - _x; + final double yDiff = referenceLoc.getY() - _y; + + final double yxRation = Math.abs(xDiff / yDiff); + + final int y = (int) (distance / (yxRation + 1)); + final int x = (int) (y * yxRation); + + _x += (xDiff < 0 ? x : -x); + _y += (yDiff < 0 ? y : -y); + } + + public void clean() + { + _x = 0; + _y = 0; + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @return The distance between this {@Point2D} and some given coordinates. + */ + public double distance2D(int x, int y) + { + final double dx = (double) _x - x; + final double dy = (double) _y - y; + + return Math.sqrt((dx * dx) + (dy * dy)); + } + + /** + * @param point : The {@link Point2D} to test. + * @return The distance between this {@Point2D} and the {@link Point2D} set as parameter. + */ + public double distance2D(Point2D point) + { + return distance2D(point.getX(), point.getY()); + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of some given coordinates. + */ + public boolean isIn2DRadius(int x, int y, int radius) + { + return distance2D(x, y) < radius; + } + + /** + * @param point : The Point2D to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of the {@link Point2D} set as parameter. + */ + public boolean isIn2DRadius(Point2D point, int radius) + { + return distance2D(point) < radius; + } +} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/AttackableAI.java index 547963e3e8..dc29d4cf6e 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/AttackableAI.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/ai/AttackableAI.java @@ -578,7 +578,7 @@ public class AttackableAI extends CreatureAI } // Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast) - final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); + final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); moveTo(moveLoc.getX(), moveLoc.getY(), moveLoc.getZ()); } } @@ -801,7 +801,7 @@ public class AttackableAI extends CreatureAI final int newZ = npc.getZ() + 30; // Mobius: Verify destination. Prevents wall collision issues and fixes monsters not avoiding obstacles. - moveTo(GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); + moveTo(GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); } return; } diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/data/SpawnTable.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/data/SpawnTable.java index a81201f69e..19f0db5b3e 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/data/SpawnTable.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/data/SpawnTable.java @@ -114,8 +114,8 @@ public class SpawnTable } // XML file for spawn - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); // Write info to XML @@ -192,8 +192,8 @@ public class SpawnTable if (update) { - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = spawn.getNpcSpawnTemplate() != null ? spawn.getNpcSpawnTemplate().getSpawnTemplate().getFile() : new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); final File tempFile = new File(spawnFile.getAbsolutePath().substring(Config.DATAPACK_ROOT.getAbsolutePath().length() + 1).replace('\\', '/') + ".tmp"); try diff --git a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/enums/GeoType.java similarity index 69% rename from L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java rename to L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/enums/GeoType.java index 7223b5b2be..f77aa5eac4 100644 --- a/L2J_Mobius_1.0_Ertheia/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/enums/GeoType.java @@ -14,20 +14,22 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ -package org.l2jmobius.gameserver.geoengine.pathfinding; +package org.l2jmobius.gameserver.enums; -/** - * @author -Nemesiss- - */ -public abstract class AbstractNodeLoc +public enum GeoType { - public abstract int getX(); + L2J("%d_%d.l2j"), + L2OFF("%d_%d_conv.dat"); - public abstract int getY(); + private final String _filename; - public abstract int getZ(); + private GeoType(String filename) + { + _filename = filename; + } - public abstract int getNodeX(); - - public abstract int getNodeY(); -} + public String getFilename() + { + return _filename; + } +} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java new file mode 100644 index 0000000000..e62f50a8df --- /dev/null +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java @@ -0,0 +1,144 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 9d255a5f54..7bef9c770b 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,72 +16,63 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.channels.FileChannel.MapMode; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.logging.Level; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; import java.util.logging.Logger; import org.l2jmobius.Config; +import org.l2jmobius.commons.util.CommonUtil; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; -import org.l2jmobius.gameserver.geoengine.geodata.IRegion; -import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; -import org.l2jmobius.gameserver.geoengine.geodata.Region; +import org.l2jmobius.gameserver.enums.GeoType; +import org.l2jmobius.gameserver.enums.MoveDirectionType; +import org.l2jmobius.gameserver.geoengine.geodata.ABlock; +import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; +import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; +import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; +import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.model.interfaces.ILocational; -import org.l2jmobius.gameserver.util.GeoUtils; -import org.l2jmobius.gameserver.util.LinePointIterator; -import org.l2jmobius.gameserver.util.LinePointIterator3D; -/** - * @author -Nemesiss-, HorridoJoho - */ public class GeoEngine { - private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - - private static final int WORLD_MIN_X = -655360; - private static final int WORLD_MIN_Y = -589824; - private static final int WORLD_MIN_Z = -16384; - - /** 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; - - /** The regions array */ - private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; - private static final int ELEVATED_SEE_OVER_DISTANCE = 2; - private static final int MAX_SEE_OVER_HEIGHT = 48; - protected GeoEngine() + private final ABlock[][] _blocks; + private final BlockNull _nullBlock; + + // Pre-allocated buffers. + private final BufferHolder[] _buffers; + + public GeoEngine() { LOGGER.info("GeoEngine: Initializing..."); - for (int i = 0; i < _regions.length(); i++) - { - _regions.set(i, NullRegion.INSTANCE); - } + // Initialize block container. + _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; + + // Load null block. + _nullBlock = new BlockNull(); + + // Initialize multilayer temporarily buffer. + BlockMultilayer.initialize(); + + // Load geo files according to geoengine config setup. int loaded = 0; try { @@ -92,23 +83,52 @@ public class GeoEngine final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); if (Files.exists(geoFilePath)) { - try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + // Region file is load-able, try to load it. + if (loadGeoBlocks(regionX, regionY)) { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); loaded++; } } + else + { + // Region file is not load-able, load null blocks. + loadNullBlocks(regionX, regionY); + } } } } catch (Exception e) { - LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + LOGGER.warning("GeoEngine: Failed to load geodata! " + e); System.exit(1); } - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + // Release multilayer block temporarily buffer. + BlockMultilayer.release(); + + String[] array = Config.PATHFIND_BUFFERS.split(";"); + _buffers = new BufferHolder[array.length]; + + int count = 0; + for (int i = 0; i < array.length; i++) + { + String buf = array[i]; + String[] args = buf.split("x"); + + try + { + int size = Integer.parseInt(args[1]); + count += size; + _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); + } + catch (Exception e) + { + LOGGER.warning("Could not load buffer setting:" + buf + ". " + e); + } + } + LOGGER.info("Loaded " + count + " node buffers."); + // Avoid wrong configs when no files are loaded. if ((loaded == 0) && Config.PATHFINDING) { @@ -118,616 +138,913 @@ public class GeoEngine } /** - * @param geoX - * @param geoY - * @return the region + * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer and log this situation. + * @param size : pre-calculated minimal required size + * @return NodeBuffer : buffer */ - private IRegion getRegion(int geoX, int geoY) + private NodeBuffer getBuffer(int size) { - final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); - if ((region < 0) || (region >= _regions.length())) + NodeBuffer current = null; + for (BufferHolder holder : _buffers) { - return null; - } - return _regions.get(region); - } - - /** - * @param filePath - * @param regionX - * @param regionY - * @throws IOException - */ - 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()).load().order(ByteOrder.LITTLE_ENDIAN))); - } - } - - /** - * @param regionX - * @param regionY - */ - public void unloadRegion(int regionX, int regionY) - { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); - } - - /** - * @param geoX - * @param geoY - * @return if geodata exist - */ - public boolean hasGeoPos(int geoX, int geoY) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return false; - } - return region.hasGeo(); - } - - /** - * Checks the specified position for available geodata. - * @param x the world x - * @param y the world y - * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise - */ - public boolean hasGeo(int x, int y) - { - return hasGeoPos(getGeoX(x), getGeoY(y)); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe check - */ - public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return true; - } - return region.checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe anti-corner cut - */ - public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) - { - boolean can = true; - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - return can && checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the nearest Z value - */ - public int getNearestZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNearestZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next lower Z value - */ - public int getNextLowerZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextLowerZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next higher Z value - */ - public int getNextHigherZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextHigherZ(geoX, geoY, worldZ); - } - - /** - * Gets the Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHeight(int x, int y, int z) - { - return getNearestZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next lower Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getLowerHeight(int x, int y, int z) - { - return getNextLowerZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next higher Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHigherHeight(int x, int y, int z) - { - return getNextHigherZ(getGeoX(x), getGeoY(y), z); - } - - /** - * @param worldX - * @return the geo X - */ - public int getGeoX(int worldX) - { - return (worldX - WORLD_MIN_X) / 16; - } - - /** - * @param worldY - * @return the geo Y - */ - public int getGeoY(int worldY) - { - return (worldY - WORLD_MIN_Y) / 16; - } - - /** - * @param worldZ - * @return the geo Z - */ - public int getGeoZ(int worldZ) - { - return (worldZ - WORLD_MIN_Z) / 16; - } - - /** - * @param geoX - * @return the world X - */ - public int getWorldX(int geoX) - { - return (geoX * 16) + WORLD_MIN_X + 8; - } - - /** - * @param geoY - * @return the world Y - */ - public int getWorldY(int geoY) - { - return (geoY * 16) + WORLD_MIN_Y + 8; - } - - /** - * @param geoZ - * @return the world Z - */ - public int getWorldZ(int geoZ) - { - return (geoZ * 16) + WORLD_MIN_Z + 8; - } - - /** - * Can see target. Doors as target always return true. Checks doors between. - * @param cha the character - * @param target the target - * @return {@code true} if the character can see the target (LOS), {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, WorldObject target) - { - if (target.isDoor()) - { - // Can always see doors. - return true; - } - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); - } - - /** - * Can see target. Checks doors between. - * @param cha the character - * @param worldPosition the world position - * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) - { - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param world - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @param tworld the target's instanceId - * @return - */ - public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) - { - return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instance - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) - { - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) - { - return false; - } - return canSeeTarget(x, y, z, tx, ty, tz); - } - - /** - * @param prevX - * @param prevY - * @param prevGeoZ - * @param curX - * @param curY - * @param nswe - * @return the LOS Z value - */ - private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) - { - if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) - { - throw new RuntimeException("Multiple directions!"); - } - - if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) - { - return getNearestZ(curX, curY, prevGeoZ); - } - - return getNextHigherZ(curX, curY, prevGeoZ); - } - - /** - * Can see target. Does not check doors between. - * @param xValue the x coordinate - * @param yValue the y coordinate - * @param zValue the z coordinate - * @param txValue the target's x coordinate - * @param tyValue the target's y coordinate - * @param tzValue the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) - { - int x = xValue; - int y = yValue; - int tx = txValue; - int ty = tyValue; - - int geoX = getGeoX(x); - int geoY = getGeoY(y); - int tGeoX = getGeoX(tx); - int tGeoY = getGeoY(ty); - - int z = getNearestZ(geoX, geoY, zValue); - int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - // Fastpath. - if ((geoX == tGeoX) && (geoY == tGeoY)) - { - if (hasGeoPos(tGeoX, tGeoY)) - { - return z == tz; - } - return true; - } - - if (tz > z) - { - int tmp = tx; - tx = x; - x = tmp; - - tmp = ty; - ty = y; - y = tmp; - - tmp = tz; - tz = z; - z = tmp; - - tmp = tGeoX; - tGeoX = geoX; - geoX = tmp; - - tmp = tGeoY; - tGeoY = geoY; - geoY = tmp; - } - - final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); - // First point is guaranteed to be available, skip it, we can always see our own position. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - final int prevZ = pointIter.z(); - int prevGeoZ = prevZ; - int ptIndex = 0; - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - - if ((curX == prevX) && (curY == prevY)) + // Find proper size of buffer. + if (holder._size < size) { continue; } - final int beeCurZ = pointIter.z(); - int curGeoZ = prevGeoZ; - - // Check if the position has geodata. - if (hasGeoPos(curX, curY)) + // Find unlocked NodeBuffer. + for (NodeBuffer buffer : holder._buffer) { - final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); - curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); - int maxHeight; - if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) + if (!buffer.isLocked()) { - maxHeight = z + MAX_SEE_OVER_HEIGHT; - } - else - { - maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; + continue; } - boolean canSeeThrough = false; - if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) + return buffer; + } + + LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); + + // NodeBuffer not found, allocate temporary buffer. + current = new NodeBuffer(holder._size); + current.isLocked(); + } + + return current; + } + + /** + * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + * @return boolean : True, when geodata file was loaded without problem. + */ + private boolean loadGeoBlocks(int regionX, int regionY) + { + final String filename = String.format(Config.GEODATA_TYPE.getFilename(), regionX, regionY); + final String filepath = Config.GEODATA_PATH + "\\" + filename; + + // Standard load. + try (RandomAccessFile raf = new RandomAccessFile(filepath, "r"); + FileChannel fc = raf.getChannel()) + { + // Initialize file buffer. + MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // Load 18B header for L2OFF geodata (1st and 2nd byte...region X and Y). + if (Config.GEODATA_TYPE == GeoType.L2OFF) + { + for (int i = 0; i < 18; i++) { - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) + buffer.get(); + } + } + + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Loop over region blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + if (Config.GEODATA_TYPE == GeoType.L2J) { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) - { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); + // Get block type. + final byte type = buffer.get(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + case GeoStructure.TYPE_MULTILAYER_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + default: + { + throw new IllegalArgumentException("Unknown block type: " + type); + } + } } else { - canSeeThrough = true; + // Get block type. + final short type = buffer.getShort(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + default: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + } } } + } + + // Check data consistency. + if (buffer.remaining() > 0) + { + LOGGER.warning("Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); + } + + // Loading was successful. + return true; + } + catch (Exception e) + { + // An error occured while loading, load null blocks. + LOGGER.warning("Error loading " + filename + " region file. " + e); + + // Replace whole region file with null blocks. + loadNullBlocks(regionX, regionY); + + // Loading was not successful. + return false; + } + } + + /** + * Loads null blocks. Used when no region file is detected or an error occurs during loading. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + */ + private void loadNullBlocks(int regionX, int regionY) + { + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Load all null blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + _blocks[blockX + ix][blockY + iy] = _nullBlock; + } + } + } + + /** + * Converts world X to geodata X. + * @param worldX + * @return int : Geo X + */ + public static int getGeoX(int worldX) + { + return (worldX - World.WORLD_X_MIN) >> 4; + } + + /** + * Converts world Y to geodata Y. + * @param worldY + * @return int : Geo Y + */ + public static int getGeoY(int worldY) + { + return (worldY - World.WORLD_Y_MIN) >> 4; + } + + /** + * Converts geodata X to world X. + * @param geoX + * @return int : World X + */ + public static int getWorldX(int geoX) + { + return (geoX << 4) + World.WORLD_X_MIN + 8; + } + + /** + * Converts geodata Y to world Y. + * @param geoY + * @return int : World Y + */ + public static int getWorldY(int geoY) + { + return (geoY << 4) + World.WORLD_Y_MIN + 8; + } + + /** + * Returns block of geodata on given coordinates. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return {@link ABlock} : Block of geodata. + */ + public ABlock getBlock(int geoX, int geoY) + { + final int x = geoX / GeoStructure.BLOCK_CELLS_X; + if ((x < 0) || (x >= GeoStructure.GEO_BLOCKS_X)) + { + return null; + } + final int y = geoY / GeoStructure.BLOCK_CELLS_Y; + if ((y < 0) || (y >= GeoStructure.GEO_BLOCKS_Y)) + { + return null; + } + return _blocks[x][y]; + } + + /** + * Check if geo coordinates has geo. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return boolean : True, if given geo coordinates have geodata + */ + public boolean hasGeoPos(int geoX, int geoY) + { + final ABlock block = getBlock(geoX, geoY); + return (block != null) && block.hasGeoPos(); + } + + /** + * Returns the height of cell, which is closest to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell geodata Z coordinate, closest to given coordinates. + */ + public short getHeightNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return (short) worldZ; + } + return block.getHeightNearest(geoX, geoY, worldZ); + } + + /** + * Returns the NSWE flag byte of cell, which is closes to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. + */ + public byte getNsweNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return GeoStructure.CELL_FLAG_ALL; + } + return block.getNsweNearest(geoX, geoY, worldZ); + } + + /** + * Check if world coordinates has geo. + * @param worldX : World X + * @param worldY : World Y + * @return boolean : True, if given world coordinates have geodata + */ + public boolean hasGeo(int worldX, int worldY) + { + return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param loc : The location used as reference. + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(Location loc) + { + return getHeightNearest(getGeoX(loc.getX()), getGeoY(loc.getY()), loc.getZ()); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param worldX : world x + * @param worldY : world y + * @param worldZ : world z + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(int worldX, int worldY, int worldZ) + { + return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); + } + + /** + * Check line of sight from {@link WorldObject} to {@link WorldObject}.
+ * @param object : The origin object. + * @param target : The target object. + * @return True, when object can see target. + */ + public boolean canSeeTarget(WorldObject object, WorldObject target) + { + // Can always see doors. + if (target.isDoor()) + { + return true; + } + + if (object.getInstanceWorld() != target.getInstanceWorld()) + { + return false; + } + + if (DoorData.getInstance().checkIfDoorsBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld())) + { + return false; + } + + // Get object's and target's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + double theight = 0; + if (target instanceof Creature) + { + theight += (((Creature) target).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + return canSee(object.getX(), object.getY(), object.getZ(), oheight, target.getX(), target.getY(), target.getZ(), theight) && canSee(target.getX(), target.getY(), target.getZ(), theight, object.getX(), object.getY(), object.getZ(), oheight); + } + + /** + * Check line of sight from {@link WorldObject} to {@link Location}.
+ * Note: The check uses {@link Location}'s real Z coordinate (e.g. point above ground), not its geodata representation. + * @param object : The origin object. + * @param position : The target position. + * @return True, when object can see position. + */ + public boolean canSeeLocation(WorldObject object, Location position) + { + // Get object and location coordinates. + int ox = object.getX(); + int oy = object.getY(); + int oz = object.getZ(); + int tx = position.getX(); + int ty = position.getY(); + int tz = position.getZ(); + + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld())) + { + return false; + } + + // Get object's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + // Perform geodata check. + return canSee(ox, oy, oz, oheight, tx, ty, tz, 0) && canSee(tx, ty, tz, 0, ox, oy, oz, oheight); + } + + /** + * Simple check for origin to target visibility.
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param oheight : The height of origin, used as start point. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param theight : The height of target, used as end point. + * @return True, when origin can see target. + */ + public boolean canSee(int ox, int oy, int oz, double oheight, int tx, int ty, int tz, double theight) + { + // Get origin geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get target geodata coordinates. + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check being on same cell and layer (index). + // Note: Get index must use origin height increased by cell height, the method returns index to height exclusive self. + int index = block.getIndexBelow(gox, goy, oz + GeoStructure.CELL_HEIGHT); + if (index < 0) + { + return false; + } + + if ((gox == gtx) && (goy == gty)) + { + return index == block.getIndexBelow(gtx, gty, tz + GeoStructure.CELL_HEIGHT); + } + + // Get ground and nswe flag. + int groundZ = block.getHeight(index); + int nswe = block.getNswe(index); + + // Get delta coordinates, slope of line (XY, XZ) and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double dz = (tz + theight) - (oz + oheight); + final double m = (double) dy / dx; + final double mz = dz / Math.sqrt((dx * dx) + (dy * dy)); + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); + + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) + { + // Set next cell in X direction. + gridX += mdt.getStepX(); + gox += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); - if (!canSeeThrough) - { - return false; - } + // Set next cell in Y direction. + gridY += mdt.getStepY(); + goy += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevGeoZ = curGeoZ; - ++ptIndex; + // Get block of the next cell. + block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get line of sight height (including Z slope). + double losz = oz + oheight + Config.MAX_OBSTACLE_HEIGHT; + losz += mz * Math.sqrt(((checkX - ox) * (checkX - ox)) + ((checkY - oy) * (checkY - oy))); + + // Check line of sight going though wall (vertical check). + + // Get index of particular layer, based on last iterated cell conditions. + boolean canMove = (nswe & dir) != 0; + if (canMove) + { + // No wall present, get next cell below current cell. + index = block.getIndexBelow(gox, goy, groundZ + GeoStructure.CELL_IGNORE_HEIGHT); + } + else + { + // Wall present, get next cell above current cell. + index = block.getIndexAbove(gox, goy, groundZ - (2 * GeoStructure.CELL_HEIGHT)); + } + + // Next cell's does not exist (no geodata with valid condition), return fail. + if (index < 0) + { + return false; + } + + // Get next cell's layer height. + int z = block.getHeight(index); + + // Perform sine of sight check (next cell is above line of sight line), return fail. + if (z > losz) + { + return false; + } + + // Next cell is accessible, update z and NSWE. + groundZ = z; + nswe = block.getNswe(index); } + // Iteration is completed, no obstacle is found. return true; } /** - * Move check. - * @param x the x coordinate - * @param y the y coordinate - * @param zValue the z coordinate - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tzValue the target's z coordinate - * @param instance the instance - * @return the last Location (x,y,z) where player can walk - just before wall + * Check movement from coordinates to coordinates.
+ * Note: The Z coordinates are supposed to be already validated geodata coordinates. + * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return True, when target coordinates are reachable from origin coordinates. */ - public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(x); - final int geoY = getGeoY(y); - final int z = getNearestZ(geoX, geoY, zValue); - final int tGeoX = getGeoX(tx); - final int tGeoY = getGeoY(ty); - final int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return new Location(x, y, getHeight(x, y, z)); - } - if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) - { - return new Location(x, y, getHeight(x, y, z)); + return false; } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = z; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return false; + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + int goz = getHeightNearest(gox, goy, oz); + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check movement within same cell. + if ((gox == gtx) && (goy == gty)) + { + return goz == getHeight(tx, ty, tz); + } + + // Get nswe flag. + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - // Can't move, return previous location. - return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check point heading into obstacle, if so return current point. + if ((nswe & dir) == 0) + { + return false; + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return false; + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != tz)) - { - // Different floors, return start location. - return new Location(x, y, z); - } - - return new Location(tx, ty, tz); + // When origin Z is target Z, the move is successful. + return goz == getHeight(tx, ty, tz); } /** - * Checks if its possible to move from one location to another. - * @param fromX the X coordinate to start checking from - * @param fromY the Y coordinate to start checking from - * @param fromZvalue the Z coordinate to start checking from - * @param toX the X coordinate to end checking at - * @param toY the Y coordinate to end checking at - * @param toZvalue the Z coordinate to end checking at - * @param instance the instance - * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + * Check movement from origin to target coordinates. Returns last available point in the checked path.
+ * Target X and Y reachable and Z is on same floor: + *
    + *
  • Location of the target with corrected Z value from geodata.
  • + *
+ * Target X and Y reachable but Z is on another floor: + *
    + *
  • Location of the origin with corrected Z value from geodata.
  • + *
+ * Target X and Y not reachable: + *
    + *
  • Last accessible location in destination to target.
  • + *
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return The {@link Location} representing last point of movement (e.g. just before wall). */ - public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + public Location getValidLocation(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(fromX); - final int geoY = getGeoY(fromY); - final int fromZ = getNearestZ(geoX, geoY, fromZvalue); - final int tGeoX = getGeoX(toX); - final int tGeoY = getGeoY(toY); - final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); - - if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) - { - return false; + return new Location(ox, oy, oz); } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = fromZ; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return new Location(ox, oy, oz); + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + final int gtz = getHeightNearest(gtx, gty, tz); + int goz = getHeightNearest(gox, goy, oz); + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - return false; - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check target cell is out of geodata grid (world coordinates). + if ((nx < 0) || (nx >= GeoStructure.GEO_CELLS_X) || (ny < 0) || (ny >= GeoStructure.GEO_CELLS_Y)) + { + return new Location(checkX, checkY, goz); + } + + // Check point heading into obstacle, if so return current (border) point. + if ((nswe & dir) == 0) + { + return new Location(checkX, checkY, goz); + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current (border) point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return new Location(checkX, checkY, goz); + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) - { - // Different floors. - return false; - } - - return true; + // Compare Z coordinates: + // If same, path is okay, return target point and fix its Z geodata coordinate. + // If not same, path is does not exist, return origin point. + return goz == gtz ? new Location(tx, ty, gtz) : new Location(ox, oy, oz); } + /** + * Returns the list of location objects as a result of complete path calculation. + * @param ox : origin x + * @param oy : origin y + * @param oz : origin z + * @param tx : target x + * @param ty : target y + * @param tz : target z + * @param instance + * @return {@code List} : complete path from nodes + */ + public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + { + // Get origin and check existing geo coords. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + if (!hasGeoPos(gox, goy)) + { + return Collections.emptyList(); + } + + int goz = getHeightNearest(gox, goy, oz); + + // Get target and check existing geo coords. + int gtx = getGeoX(tx); + int gty = getGeoY(ty); + if (!hasGeoPos(gtx, gty)) + { + return Collections.emptyList(); + } + + int gtz = getHeightNearest(gtx, gty, tz); + + // Prepare buffer for pathfinding calculations. + NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + if (buffer == null) + { + return Collections.emptyList(); + } + + // Find path. + List path = null; + try + { + path = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + if (path.isEmpty()) + { + return Collections.emptyList(); + } + } + catch (Exception e) + { + return Collections.emptyList(); + } + finally + { + buffer.free(); + } + + // Check path. + if (path.size() < 3) + { + return path; + } + + // Get path list iterator. + ListIterator point = path.listIterator(); + + // Get node A (origin). + int nodeAx = ox; + int nodeAy = oy; + int nodeAz = goz; + + // Get node B. + Location nodeB = point.next(); + + // Iterate thought the path to optimize it. + while (point.hasNext()) + { + // Get node C. + Location nodeC = path.get(point.nextIndex()); + + // Check movement from node A to node C. + if (canMoveToTarget(nodeAx, nodeAy, nodeAz, nodeC.getX(), nodeC.getY(), nodeC.getZ(), instance)) + { + // Can move from node A to node C. + // Remove node B. + point.remove(); + } + else + { + // Can not move from node A to node C. + // Set node A (node B is part of path, update A coordinates). + nodeAx = nodeB.getX(); + nodeAy = nodeB.getY(); + nodeAz = nodeB.getZ(); + } + + // Set node B. + nodeB = point.next(); + } + + return path; + } + + /** + * NodeBuffer container with specified size and count of separate buffers. + */ + private static class BufferHolder + { + final int _size; + final List _buffer; + + public BufferHolder(int size, int count) + { + _size = size; + _buffer = new ArrayList<>(count); + + for (int i = 0; i < count; i++) + { + _buffer.add(new NodeBuffer(size)); + } + } + } + + /** + * Returns the instance of the {@link GeoEngine}. + * @return {@link GeoEngine} : The instance. + */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -737,4 +1054,4 @@ public class GeoEngine { protected static final GeoEngine INSTANCE = new GeoEngine(); } -} +} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java deleted file mode 100644 index cc76b7523a..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ /dev/null @@ -1,301 +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 . - */ -package org.l2jmobius.gameserver.geoengine; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; -import org.l2jmobius.gameserver.model.World; -import org.l2jmobius.gameserver.model.instancezone.Instance; - -/** - * @author -Nemesiss- - */ -public class GeoEnginePathfinding -{ - private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); - - private BufferInfo[] _buffers; - - protected GeoEnginePathfinding() - { - try - { - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - - _buffers = 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); - } - - _buffers[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"); - } - } - - public boolean pathNodesExist(short regionoffset) - { - return false; - } - - public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) - { - 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)))); - if (buffer == null) - { - return null; - } - - List path = null; - try - { - final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); - - if (result == null) - { - return null; - } - - path = constructPath(result); - } - catch (Exception e) - { - LOGGER.warning(e.getMessage()); - return null; - } - finally - { - buffer.free(); - } - - // check path - if (path.size() < 3) - { - return path; - } - - int currentX, currentY, currentZ; - ListIterator middlePoint; - - middlePoint = path.listIterator(); - currentX = x; - currentY = y; - currentZ = z; - - while (middlePoint.hasNext()) - { - final AbstractNodeLoc locMiddle = middlePoint.next(); - if (!middlePoint.hasNext()) - { - break; - } - - final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); - if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) - { - middlePoint.remove(); - } - else - { - currentX = locMiddle.getX(); - currentY = locMiddle.getY(); - currentZ = locMiddle.getZ(); - } - } - - return path; - } - - /** - * Convert geodata position to pathnode position - * @param geo_pos - * @return pathnode position - */ - public short getNodePos(int geo_pos) - { - return (short) (geo_pos >> 3); // OK? - } - - /** - * Convert node position to pathnode block position - * @param node_pos - * @return pathnode block position (0...255) - */ - public short getNodeBlock(int node_pos) - { - return (short) (node_pos % 256); - } - - public byte getRegionX(int node_pos) - { - return (byte) ((node_pos >> 8) + World.TILE_X_MIN); - } - - public byte getRegionY(int node_pos) - { - return (byte) ((node_pos >> 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 node_x rx - * @return - */ - public int calculateWorldX(short node_x) - { - return World.MAP_MIN_X + (node_x * 128) + 48; - } - - /** - * Convert pathnode y to World y position - * @param node_y - * @return - */ - public int calculateWorldY(short node_y) - { - return World.MAP_MIN_Y + (node_y * 128) + 48; - } - - private List constructPath(AbstractNode nodeValue) - { - final LinkedList path = new LinkedList<>(); - int previousDirectionX = Integer.MIN_VALUE; - int previousDirectionY = Integer.MIN_VALUE; - int directionX, directionY; - - AbstractNode node = nodeValue; - while (node.getParent() != null) - { - directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); - directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); - - // only add a new route point if moving direction changes - if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) - { - previousDirectionX = directionX; - previousDirectionY = directionY; - - path.addFirst(node.getLoc()); - node.setLoc(null); - } - - node = node.getParent(); - } - - return path; - } - - private CellNodeBuffer alloc(int size) - { - CellNodeBuffer current = null; - for (BufferInfo i : _buffers) - { - if (i.mapSize >= size) - { - for (CellNodeBuffer buf : i.buffer) - { - if (buf.lock()) - { - current = buf; - break; - } - } - if (current != null) - { - break; - } - - // not found, allocate temporary buffer - current = new CellNodeBuffer(i.mapSize); - current.lock(); - if (i.buffer.size() < i.count) - { - i.buffer.add(current); - break; - } - } - } - - return current; - } - - private static final class BufferInfo - { - final int mapSize; - final int count; - ArrayList buffer; - - public BufferInfo(int size, int cnt) - { - mapSize = size; - count = cnt; - buffer = new ArrayList<>(count); - } - } - - public static GeoEnginePathfinding getInstance() - { - return SingletonHolder.INSTANCE; - } - - private static class SingletonHolder - { - protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); - } -} diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java new file mode 100644 index 0000000000..49ec35aa1c --- /dev/null +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java @@ -0,0 +1,85 @@ +/* + * 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 . + */ +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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @param index : Index of the cell. + * @return short : Cell geodata Z coordinate, below given coordinates. + */ + public abstract byte getNswe(int index); +} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java new file mode 100644 index 0000000000..f5997bf287 --- /dev/null +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java @@ -0,0 +1,130 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java new file mode 100644 index 0000000000..9af9d2a28a --- /dev/null +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java @@ -0,0 +1,95 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java new file mode 100644 index 0000000000..31fbf5d477 --- /dev/null +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java similarity index 55% rename from L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java rename to L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java index 72a5a635c7..f77d21d84b 100644 --- a/L2J_Mobius_2.5_Underground/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java @@ -16,40 +16,53 @@ */ package org.l2jmobius.gameserver.geoengine.geodata; -/** - * @author HorridoJoho - */ -public final class NullRegion implements IRegion +public class BlockNull extends ABlock { - public static final NullRegion INSTANCE = new NullRegion(); - @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 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() + public boolean hasGeoPos() { 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java deleted file mode 100644 index 61ea4fcf82..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java +++ /dev/null @@ -1,48 +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 . - */ -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() - { - } -} diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java deleted file mode 100644 index 3c8de1a7f7..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java +++ /dev/null @@ -1,77 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java deleted file mode 100644 index 1ccdefe3da..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java +++ /dev/null @@ -1,56 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java new file mode 100644 index 0000000000..691b164e0c --- /dev/null +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ +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_ALL = 0x0F; + + // 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; +} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java deleted file mode 100644 index 25eafc5a04..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java +++ /dev/null @@ -1,42 +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 . - */ -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); -} diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java deleted file mode 100644 index 0d2c765002..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java +++ /dev/null @@ -1,47 +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 . - */ -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(); -} diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java deleted file mode 100644 index 52df457360..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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); - - // 1 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; - } -} diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java deleted file mode 100644 index fc924cb875..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java +++ /dev/null @@ -1,92 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java deleted file mode 100644 index 5087513ed9..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java +++ /dev/null @@ -1,87 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public abstract class AbstractNode -{ - private T _loc; - private AbstractNode _parent; - - public AbstractNode(T loc) - { - _loc = loc; - } - - public void setParent(AbstractNode p) - { - _parent = p; - } - - public AbstractNode getParent() - { - return _parent; - } - - public T getLoc() - { - return _loc; - } - - public void setLoc(T l) - { - _loc = l; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); - return result; - } - - @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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java deleted file mode 100644 index 3425234e0e..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public class CellNode extends AbstractNode -{ - 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java deleted file mode 100644 index 522610bb7c..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java +++ /dev/null @@ -1,348 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -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 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) - { - _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(); - } - - public final List debugPath() - { - final List 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 void getNeighbors() - { - if (!_current.getLoc().canGoAll()) - { - 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); - } - - // SouthEast - if ((nodeE != null) && (nodeS != null)) - { - if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) - { - addNode(x + 1, y + 1, z, true); - } - } - - // SouthWest - if ((nodeS != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) - { - addNode(x - 1, y + 1, z, true); - } - } - - // NorthEast - if ((nodeN != null) && (nodeE != null)) - { - if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) - { - addNode(x + 1, y - 1, z, true); - } - } - - // NorthWest - if ((nodeN != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) - { - addNode(x - 1, y - 1, z, true); - } - } - } - - private 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(); - // reinit node if needed - if (result.getLoc() != null) - { - result.getLoc().set(x, y, z); - } - else - { - result.setLoc(new NodeLoc(x, y, z)); - } - } - - return result; - } - - private 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 boolean isHighWeight(int x, int y, int z) - { - final CellNode result = getNode(x, y, z); - if (result == null) - { - return true; - } - - if (!result.getLoc().canGoAll()) - { - return true; - } - if (Math.abs(result.getLoc().getZ() - z) > 16) - { - return true; - } - - return false; - } - - private 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java new file mode 100644 index 0000000000..fa5ca43c2f --- /dev/null +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -0,0 +1,111 @@ +/* + * 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 . + */ +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 geodata values. + private int _geoX; + private int _geoY; + private byte _nswe; + + // 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; + + _costG = 0; + _costH = 0; + _costF = 0; + + _parent = null; + } + + public void setGeo(int gx, int gy, int gz, byte nswe) + { + super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); + + _geoX = gx; + _geoY = gy; + _nswe = nswe; + } + + 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 int getCostF() + { + return _costF; + } + + public Node getParent() + { + return _parent; + } + + @Override + public int compareTo(Node o) + { + return _costF - o._costF; + } +} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java new file mode 100644 index 0000000000..b464212c96 --- /dev/null +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -0,0 +1,368 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.awt.Color; +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; +import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; + +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 _opened; + // Container holding Nodes already explored. + private final List _closed; + + // Target coordinates. + private int _gtx; + private int _gty; + private int _gtz; + + // Pathfinding statistics. + private long _timeStamp; + private long _lastElapsedTime; + + 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 findPath(int gox, int goy, int goz, int gtx, int gty, int gtz) + { + // Set start timestamp. + _timeStamp = System.currentTimeMillis(); + + // 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)); + _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 constructPath() + { + // Create result. + final LinkedList 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; + } + + /** + * Creates list of Nodes to show debug path. + * @param debug : The debug packet to add debug informations in. + */ + public void debugPath(ExServerPrimitive debug) + { + // Add all opened node as yellow points. + for (Node n : _opened) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); + } + + // Add all opened node as blue points. + for (Node n : _closed) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); + } + } + + 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; + + _lastElapsedTime = System.currentTimeMillis() - _timeStamp; + _lock.unlock(); + } + + public long getElapsedTime() + { + return _lastElapsedTime; + } + + /** + * Expand the current {@link Node} by exploring its neighbors (axially and diagonally). + */ + private void expand() + { + // Movement is blocked, skip. + final byte nswe = _current.getNSWE(); + if (nswe == 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; + + // Can move north, expand. + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + } + + // Can move south, expand. + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + } + + // Can move west, expand. + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move east, expand. + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move north-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move north-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + } + + /** + * 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 weight : The weight of movement to the new node. + * @return The nswe of the added node. Blank, if not added. + */ + private byte addNode(int gx, int gy, int gzValue, int weight) + { + // 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]; + + // Set node geodata coordinates. + node.setGeo(gx, gy, gz, nswe); + + // 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, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + _opened.add(node); + _bufferIndex++; + return nswe; + } + + /** + * Calculate cost H value, calculated using diagonal distance method.
+ * 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. + final int dx = Math.abs(gx - _gtx); + final int dy = Math.abs(gy - _gty); + final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + + // Get diagonal and axial differences to the target. + final int dd = Math.min(dx, dy); + final int da = Math.max(dx, dy) - dd; + + // Calculate the diagonal distance of the node to the target. + return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + } +} \ No newline at end of file diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java deleted file mode 100644 index e3bad04b25..0000000000 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; - -/** - * @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 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 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; - // return super.hashCode(); - } - - @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; - if (_x != other._x) - { - return false; - } - if (_y != other._y) - { - return false; - } - if (_goNorth != other._goNorth) - { - return false; - } - if (_goEast != other._goEast) - { - return false; - } - if (_goSouth != other._goSouth) - { - return false; - } - if (_goWest != other._goWest) - { - return false; - } - if (_geoHeight != other._geoHeight) - { - return false; - } - return true; - } -} diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java index 1faf1ae423..ed261765e1 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java @@ -93,15 +93,15 @@ public class ZoneManager implements IXmlReader private static final Map SETTINGS = new HashMap<>(); private static final int SHIFT_BY = 15; - private static final int OFFSET_X = Math.abs(World.MAP_MIN_X >> SHIFT_BY); - private static final int OFFSET_Y = Math.abs(World.MAP_MIN_Y >> SHIFT_BY); + private static final int OFFSET_X = Math.abs(World.WORLD_X_MIN >> SHIFT_BY); + private static final int OFFSET_Y = Math.abs(World.WORLD_Y_MIN >> SHIFT_BY); private final Map, ConcurrentHashMap> _classZones = new ConcurrentHashMap<>(); private final Map _spawnTerritories = new ConcurrentHashMap<>(); private final AtomicInteger _lastDynamicId = new AtomicInteger(300000); private List _debugItems; - private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.MAP_MAX_X >> SHIFT_BY) + OFFSET_X + 1][(World.MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y + 1]; + private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.WORLD_X_MAX >> SHIFT_BY) + OFFSET_X + 1][(World.WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y + 1]; /** * Instantiates a new zone manager. diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/Location.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/Location.java index 73689ab1a6..ca3b22a3f0 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/Location.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/Location.java @@ -16,30 +16,30 @@ */ package org.l2jmobius.gameserver.model; +import java.util.Objects; + +import org.l2jmobius.commons.util.Point2D; import org.l2jmobius.gameserver.model.interfaces.ILocational; import org.l2jmobius.gameserver.model.interfaces.IPositionable; /** - * Location data transfer object.
- * Contains coordinates data, heading and instance Id. - * @author Zoey76 + * A datatype used to retain a 3D (x/y/z/heading) point. It got the capability to be set and cleaned. */ -public class Location implements IPositionable +public class Location extends Point2D implements IPositionable { - protected int _x; - protected int _y; - protected int _z; - private int _heading; + protected volatile int _z; + protected volatile int _heading; public Location(int x, int y, int z) { - this(x, y, z, 0); + super(x, y); + _z = z; + _heading = 0; } public Location(int x, int y, int z, int heading) { - _x = x; - _y = y; + super(x, y); _z = z; _heading = heading; } @@ -51,9 +51,8 @@ public class Location implements IPositionable public Location(StatSet set) { - _x = set.getInt("x"); - _y = set.getInt("y"); - _z = set.getInt("z"); + super(set.getInt("x", 0), set.getInt("y", 0)); + _z = set.getInt("z", 0); _heading = set.getInt("heading", 0); } @@ -146,6 +145,25 @@ public class Location implements IPositionable _heading = loc.getHeading(); } + @Override + public void clean() + { + super.clean(); + _z = 0; + } + + @Override + public Location clone() + { + return new Location(_x, _y, _z); + } + + @Override + public int hashCode() + { + return (31 * super.hashCode()) + Objects.hash(_z); + } + @Override public boolean equals(Object obj) { diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/World.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/World.java index c1510e8c0e..0d796dd834 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/World.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/World.java @@ -66,19 +66,19 @@ public class World public static final int TILE_Y_MAX = 26; public static final int TILE_ZERO_COORD_X = 20; public static final int TILE_ZERO_COORD_Y = 18; - public static final int MAP_MIN_X = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; - public static final int MAP_MIN_Y = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; + public static final int WORLD_X_MIN = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; + public static final int WORLD_Y_MIN = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; - public static final int MAP_MAX_X = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; - public static final int MAP_MAX_Y = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; + public static final int WORLD_X_MAX = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; + public static final int WORLD_Y_MAX = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; /** Calculated offset used so top left region is 0,0 */ - public static final int OFFSET_X = Math.abs(MAP_MIN_X >> SHIFT_BY); - public static final int OFFSET_Y = Math.abs(MAP_MIN_Y >> SHIFT_BY); + public static final int OFFSET_X = Math.abs(WORLD_X_MIN >> SHIFT_BY); + public static final int OFFSET_Y = Math.abs(WORLD_Y_MIN >> SHIFT_BY); /** Number of regions. */ - private static final int REGIONS_X = (MAP_MAX_X >> SHIFT_BY) + OFFSET_X; - private static final int REGIONS_Y = (MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y; + private static final int REGIONS_X = (WORLD_X_MAX >> SHIFT_BY) + OFFSET_X; + private static final int REGIONS_Y = (WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y; /** Map containing all the players in game. */ private static final Map _allPlayers = new ConcurrentHashMap<>(); @@ -817,9 +817,6 @@ public class World return _memberInPartyNumber.get(); } - /** - * @return the current instance of World - */ public static World getInstance() { return SingletonHolder.INSTANCE; diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/WorldObject.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/WorldObject.java index 0e5536bbc0..62313ffea1 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/WorldObject.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/WorldObject.java @@ -188,23 +188,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif synchronized (this) { int spawnX = x; - if (spawnX > World.MAP_MAX_X) + if (spawnX > World.WORLD_X_MAX) { - spawnX = World.MAP_MAX_X - 5000; + spawnX = World.WORLD_X_MAX - 5000; } - if (spawnX < World.MAP_MIN_X) + if (spawnX < World.WORLD_X_MIN) { - spawnX = World.MAP_MIN_X + 5000; + spawnX = World.WORLD_X_MIN + 5000; } int spawnY = y; - if (spawnY > World.MAP_MAX_Y) + if (spawnY > World.WORLD_Y_MAX) { - spawnY = World.MAP_MAX_Y - 5000; + spawnY = World.WORLD_Y_MAX - 5000; } - if (spawnY < World.MAP_MIN_Y) + if (spawnY < World.WORLD_Y_MIN) { - spawnY = World.MAP_MIN_Y + 5000; + spawnY = World.WORLD_Y_MIN + 5000; } // Set the x,y,z position of the WorldObject. If flagged with _isSpawned, setXYZ will automatically update world region, so avoid that. @@ -518,23 +518,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif public void setXYZInvisible(int x, int y, int z) { int correctX = x; - if (correctX > World.MAP_MAX_X) + if (correctX > World.WORLD_X_MAX) { - correctX = World.MAP_MAX_X - 5000; + correctX = World.WORLD_X_MAX - 5000; } - if (correctX < World.MAP_MIN_X) + if (correctX < World.WORLD_X_MIN) { - correctX = World.MAP_MIN_X + 5000; + correctX = World.WORLD_X_MIN + 5000; } int correctY = y; - if (correctY > World.MAP_MAX_Y) + if (correctY > World.WORLD_Y_MAX) { - correctY = World.MAP_MAX_Y - 5000; + correctY = World.WORLD_Y_MAX - 5000; } - if (correctY < World.MAP_MIN_Y) + if (correctY < World.WORLD_Y_MIN) { - correctY = World.MAP_MIN_Y + 5000; + correctY = World.WORLD_Y_MIN + 5000; } setXYZ(correctX, correctY, z); diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/Creature.java index e0c108de41..33cdefc41d 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -65,8 +65,6 @@ 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.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -2585,7 +2583,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3406,8 +3404,8 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe final int originalX = x; final int originalY = y; final int originalZ = z; - final int gtx = (originalX - World.MAP_MIN_X) >> 4; - final int gty = (originalY - World.MAP_MIN_Y) >> 4; + final int gtx = (originalX - World.WORLD_X_MIN) >> 4; + final int gty = (originalY - World.WORLD_Y_MIN) >> 4; if (isOnGeodataPath()) { try @@ -3430,7 +3428,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe && !(((curZ - z) > 300) && (distance < 300))) // Prohibit correcting destination if character wants to fall. { // location different if destination wasn't reached (or just z coord is different) - final Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(curX, curY, curZ, x, y, z, getInstanceWorld()); + final Location destiny = GeoEngine.getInstance().getValidLocation(curX, curY, curZ, x, y, z, getInstanceWorld()); x = destiny.getX(); y = destiny.getY(); z = destiny.getZ(); @@ -3444,7 +3442,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { if (isPlayer() && !_isFlying && !isInWater) diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/Summon.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/Summon.java index 6065b24f61..919cda941e 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/Summon.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/actor/Summon.java @@ -106,7 +106,7 @@ public abstract class Summon extends Playable final int x = owner.getX(); final int y = owner.getY(); final int z = owner.getZ(); - final Location location = GeoEngine.getInstance().canMoveToTargetLoc(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, owner.getInstanceWorld()); + final Location location = GeoEngine.getInstance().getValidLocation(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, getInstanceWorld()); setXYZInvisible(location.getX(), location.getY(), location.getZ()); } diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java index 334bab47ea..9b0c7059f1 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java @@ -1507,7 +1507,7 @@ public class ItemInstance extends WorldObject if (dropper != null) { final Instance instance = dropper.getInstanceWorld(); - final Location dropDest = GeoEngine.getInstance().canMoveToTargetLoc(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); + final Location dropDest = GeoEngine.getInstance().getValidLocation(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); x = dropDest.getX(); y = dropDest.getY(); z = dropDest.getZ(); diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java index 4771ad5957..ef1fd785d7 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java @@ -1179,7 +1179,7 @@ public class SkillCaster implements Runnable } } - final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().canMoveToTargetLoc(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); + final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().getValidLocation(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); creature.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); creature.broadcastPacket(new FlyToLocation(creature, destination, flyType, 0, 0, 333)); diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java index c62c821f44..30068134e3 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java @@ -114,7 +114,7 @@ public class ValidatePosition implements IClientIncomingPacket { if (player.isFalling(_z)) { - final int nearestZ = GeoEngine.getInstance().getHigherHeight(_x, _y, _z); + final int nearestZ = GeoEngine.getInstance().getHeight(_x, _y, _z); if (player.getZ() < nearestZ) { player.setXYZ(_x, _y, nearestZ); diff --git a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/util/GeoUtils.java index 4600a0fc53..19a979819e 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -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.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; @@ -30,21 +30,21 @@ public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getInstance().getWorldX(iter.x()); - final int wy = GeoEngine.getInstance().getWorldY(iter.y()); + final int wx = GeoEngine.getWorldX(iter.x()); + final int wy = GeoEngine.getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public final class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); - int wy = GeoEngine.getInstance().getWorldY(prevY); + int wx = GeoEngine.getWorldX(prevX); + int wy = GeoEngine.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.getInstance().getWorldX(curX); - wy = GeoEngine.getInstance().getWorldY(curY); + wx = GeoEngine.getWorldX(curX); + wy = GeoEngine.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().checkNearestNswe(x, y, z, nswe)) + if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0) { return Color.GREEN; } @@ -109,9 +109,8 @@ public final class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final GeoEngine ge = GeoEngine.getInstance(); - final int playerGx = ge.getGeoX(player.getX()); - final int playerGy = ge.getGeoY(player.getY()); + final int playerGx = GeoEngine.getGeoX(player.getX()); + final int playerGy = GeoEngine.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -135,32 +134,32 @@ public final class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = ge.getWorldX(gx); - final int y = ge.getWorldY(gy); - final int z = ge.getNearestZ(gx, gy, player.getZ()); + final int x = GeoEngine.getWorldX(gx); + final int y = GeoEngine.getWorldY(gy); + final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); + Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); 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, Cell.NSWE_EAST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); 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, Cell.NSWE_SOUTH); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); 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, Cell.NSWE_WEST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); 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); @@ -188,41 +187,41 @@ public final class GeoUtils { if (y > lastY) { - return Cell.NSWE_SOUTH_EAST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E; } else if (y < lastY) { - return Cell.NSWE_NORTH_EAST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E; } else { - return Cell.NSWE_EAST; + return GeoStructure.CELL_FLAG_E; } } else if (x < lastX) // west { if (y > lastY) { - return Cell.NSWE_SOUTH_WEST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W; } else if (y < lastY) { - return Cell.NSWE_NORTH_WEST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W; } else { - return Cell.NSWE_WEST; + return GeoStructure.CELL_FLAG_W; } } else // unchanged x { if (y > lastY) { - return Cell.NSWE_SOUTH; + return GeoStructure.CELL_FLAG_S; } else if (y < lastY) { - return Cell.NSWE_NORTH; + return GeoStructure.CELL_FLAG_N; } else { diff --git a/L2J_Mobius_5.0_Salvation/dist/game/config/GeoEngine.ini b/L2J_Mobius_5.0_Salvation/dist/game/config/GeoEngine.ini index 609e8a89a7..e740c678dd 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_5.0_Salvation/dist/game/config/GeoEngine.ini @@ -6,33 +6,44 @@ # at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ 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 + # ================================================================= -# Path finding +# Pathfinding # ================================================================= # 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 +# an alternative path (e.g. walk around obstacle), default: True +PathFinding = True -# Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 -PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 +# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 -# Weight for nodes without obstacles far from walls -LowWeight = 0.5 +# Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 +MoveWeight = 10 +MoveWeightDiag = 14 -# Weight for nodes near walls -MediumWeight = 2 +# 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 -# Weight for nodes with obstacles -HighWeight = 3 +# 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. -# Default: LowWeight * sqrt(2) -DiagonalWeight = 0.707 +# Maximum number of generated nodes per one path-finding process, default 3500 +MaxIterations = 3500 # ================================================================= -# Other +# Line of Sight # ================================================================= -# Correct player Z after falling through the ground. -CorrectPlayerZ = False +# 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 diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/geodata/L2D_GeoEngine.patch b/L2J_Mobius_5.0_Salvation/dist/game/data/geodata/L2D_GeoEngine.patch deleted file mode 100644 index f8577e3a0e..0000000000 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/geodata/L2D_GeoEngine.patch +++ /dev/null @@ -1,6035 +0,0 @@ -Index: dist/game/GeoDataConverter.bat -=================================================================== ---- dist/game/GeoDataConverter.bat (nonexistent) -+++ dist/game/GeoDataConverter.bat (working copy) -@@ -0,0 +1,6 @@ -+@echo off -+title L2D geodata converter -+ -+java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter -+ -+pause -Index: dist/game/GeoDataConverter.sh -=================================================================== ---- dist/game/GeoDataConverter.sh (nonexistent) -+++ dist/game/GeoDataConverter.sh (working copy) -@@ -0,0 +1,4 @@ -+#! /bin/sh -+ -+java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 -+ -Index: dist/game/config/GeoEngine.ini -=================================================================== ---- dist/game/config/GeoEngine.ini (revision 8409) -+++ dist/game/config/GeoEngine.ini (working copy) -@@ -1,6 +1,10 @@ - # ================================================================= - # Geodata - # ================================================================= -+# Because of real-time performance we are using geodata files only in -+# diagonal L2D format now (using filename e.g. 22_16.l2d). -+# L2D geodata can be obtained by conversion of existing L2J or L2OFF geodata. -+# Launch "GeoDataConverter.bat/sh" and follow instructions to start the conversion. - - # 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/ -@@ -13,6 +17,16 @@ - CoordSynchronize = 2 - - # ================================================================= -+# Path checking -+# ================================================================= -+ -+# 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 finding - # ================================================================= - -@@ -23,19 +37,23 @@ - # Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - --# Weight for nodes without obstacles far from walls --LowWeight = 0.5 -+# Base path weight, when moving from one node to another on axis direction, default: 10 -+BaseWeight = 10 - --# Weight for nodes near walls --MediumWeight = 2 -+# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -+DiagonalWeight = 14 - --# Weight for nodes with obstacles --HighWeight = 3 -+# When movement flags of target node is blocked to any direction, multiply movement weight by this multiplier. -+# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 10 -+ObstacleMultiplier = 10 - --# Weight for diagonal movement. --# Default: LowWeight * sqrt(2) --DiagonalWeight = 0.707 -+# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 20 -+# For proper function must be higher than BaseWeight and/or DiagonalWeight. -+HeuristicWeight = 20 - -+# Maximum number of generated nodes per one path-finding process, default 3500 -+MaxIterations = 3500 -+ - # ================================================================= - # Other - # ================================================================= -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (working copy) -@@ -55,9 +55,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -@@ -73,9 +72,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (working copy) -@@ -18,11 +18,11 @@ - - import java.util.List; - --import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -+import org.l2jmobius.gameserver.geoengine.GeoEngine; - import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -+import org.l2jmobius.gameserver.network.SystemMessageId; - import org.l2jmobius.gameserver.util.BuilderUtil; - - public class AdminPathNode implements IAdminCommandHandler -@@ -37,29 +37,30 @@ - { - if (command.equals("admin_path_find")) - { -- if (!Config.PATHFINDING) -- { -- BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); -- return true; -- } - if (activeChar.getTarget() != null) - { -- final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); -+ final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); - if (path == null) - { -- BuilderUtil.sendSysMessage(activeChar, "No Route!"); -- return true; -+ BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); - } -- for (AbstractNodeLoc a : path) -+ else - { -- BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); -+ for (Location point : path) -+ { -+ BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); -+ } - } - } - else - { -- BuilderUtil.sendSysMessage(activeChar, "No Target!"); -+ activeChar.sendPacket(SystemMessageId.INVALID_TARGET); - } - } -+ else -+ { -+ return false; -+ } - return true; - } - -Index: java/org/l2jmobius/Config.java -=================================================================== ---- java/org/l2jmobius/Config.java (revision 8409) -+++ java/org/l2jmobius/Config.java (working copy) -@@ -32,7 +32,6 @@ - import java.net.UnknownHostException; - import java.nio.charset.StandardCharsets; - import java.nio.file.Files; --import java.nio.file.Path; - import java.nio.file.Paths; - import java.time.Duration; - import java.util.ArrayList; -@@ -966,14 +965,17 @@ - // -------------------------------------------------- - // GeoEngine - // -------------------------------------------------- -- public static Path GEODATA_PATH; -+ public static String GEODATA_PATH; -+ public static int COORD_SYNCHRONIZE; -+ public static int PART_OF_CHARACTER_HEIGHT; -+ public static int MAX_OBSTACLE_HEIGHT; - public static boolean PATHFINDING; - public static String PATHFIND_BUFFERS; -- public static float LOW_WEIGHT; -- public static float MEDIUM_WEIGHT; -- public static float HIGH_WEIGHT; -- public static float DIAGONAL_WEIGHT; -- public static int COORD_SYNCHRONIZE; -+ public static int BASE_WEIGHT; -+ public static int DIAGONAL_WEIGHT; -+ public static int HEURISTIC_WEIGHT; -+ public static int OBSTACLE_MULTIPLIER; -+ public static int MAX_ITERATIONS; - public static boolean CORRECT_PLAYER_Z; - - /** Attribute System */ -@@ -2568,14 +2570,17 @@ - - // Load GeoEngine config file (if exists) - final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); -- GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); -+ GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); -+ COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); -+ MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); - PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -- LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); -- MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); -- HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); -- DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); -- COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); -+ DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); -+ OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); -+ HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); -+ MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); - CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); - - // Load AllowedPlayerRaces config file (if exists) -Index: java/org/l2jmobius/gameserver/geoengine/GeoEngine.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (revision 8435) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (working copy) -@@ -16,100 +16,90 @@ - */ - package org.l2jmobius.gameserver.geoengine; - --import java.io.IOException; -+import java.io.File; - import java.io.RandomAccessFile; - import java.nio.ByteOrder; --import java.nio.channels.FileChannel.MapMode; --import java.nio.file.Files; --import java.nio.file.Path; --import java.util.concurrent.atomic.AtomicReferenceArray; --import java.util.logging.Level; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.List; - import java.util.logging.Logger; - - import org.l2jmobius.Config; - import org.l2jmobius.gameserver.data.xml.DoorData; - import org.l2jmobius.gameserver.data.xml.FenceData; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; --import org.l2jmobius.gameserver.geoengine.geodata.IRegion; --import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; --import org.l2jmobius.gameserver.geoengine.geodata.Region; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.World; - import org.l2jmobius.gameserver.model.WorldObject; -+import org.l2jmobius.gameserver.model.actor.Creature; - import org.l2jmobius.gameserver.model.instancezone.Instance; --import org.l2jmobius.gameserver.model.interfaces.ILocational; --import org.l2jmobius.gameserver.util.GeoUtils; --import org.l2jmobius.gameserver.util.LinePointIterator; --import org.l2jmobius.gameserver.util.LinePointIterator3D; -+import org.l2jmobius.gameserver.util.MathUtil; - - /** -- * @author -Nemesiss-, HorridoJoho -+ * @author Hasha - */ - public class GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); -+ protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - -- private static final int WORLD_MIN_X = -655360; -- private static final int WORLD_MIN_Y = -589824; -- private static final int WORLD_MIN_Z = -16384; -+ private final ABlock[][] _blocks; -+ private final BlockNull _nullBlock; - -- /** 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; -- -- /** The regions array */ -- private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); -- -- private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; -- private static final int ELEVATED_SEE_OVER_DISTANCE = 2; -- private static final int MAX_SEE_OVER_HEIGHT = 48; -- -- protected GeoEngine() -+ /** -+ * GeoEngine constructor. Loads all geodata files of chosen geodata format. -+ */ -+ public GeoEngine() - { - LOGGER.info("GeoEngine: Initializing..."); -- for (int i = 0; i < _regions.length(); i++) -- { -- _regions.set(i, NullRegion.INSTANCE); -- } - -+ // initialize block container -+ _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; -+ -+ // load null block -+ _nullBlock = new BlockNull(); -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files according to geoengine config setup - int loaded = 0; -- try -+ long fileSize = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { -- for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { -- for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) -+ final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); -+ if (f.exists() && !f.isDirectory()) - { -- final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); -- if (Files.exists(geoFilePath)) -+ // region file is load-able, try to load it -+ if (loadGeoBlocks(rx, ry)) - { -- try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) -- { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -- loaded++; -- } -+ loaded++; -+ fileSize += f.length(); - } - } -+ else -+ { -+ // region file is not load-able, load null blocks -+ loadNullBlocks(rx, ry); -+ } - } - } -- catch (Exception e) -+ -+ LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -+ if (loaded > 0) - { -- LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); -- System.exit(1); -+ LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); - } - -- LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -- -- // Avoid wrong configs when no files are loaded. -+ // avoid wrong configs when no files are loaded - if (loaded == 0) - { - if (Config.PATHFINDING) -@@ -123,619 +113,820 @@ - LOGGER.info("GeoEngine: Forcing CoordSynchronize setting to -1."); - } - } -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); - } - - /** -- * @param geoX -- * @param geoY -- * @return the region -+ * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. -+ * @return boolean : True, when geodata file was loaded without problem. - */ -- private IRegion getRegion(int geoX, int geoY) -+ private final boolean loadGeoBlocks(int regionX, int regionY) - { -- final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); -- if ((region < 0) || (region >= _regions.length())) -+ final String filename = String.format(GeoFormat.L2D.getFilename(), regionX, regionY); -+ -+ // standard load -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) - { -- return null; -+ // initialize file buffer -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, GeoFormat.L2D); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ } -+ -+ // check data consistency -+ if (buffer.remaining() > 0) -+ { -+ LOGGER.warning("GeoEngine: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ } -+ -+ // loading was successful -+ return true; - } -- return _regions.get(region); -- } -- -- /** -- * @param filePath -- * @param regionX -- * @param regionY -- * @throws IOException -- */ -- 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")) -+ catch (Exception e) - { -- _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -+ // an error occured while loading, load null blocks -+ LOGGER.warning("GeoEngine: Error while loading " + filename + " region file."); -+ LOGGER.warning(e.getMessage()); -+ -+ // replace whole region file with null blocks -+ loadNullBlocks(regionX, regionY); -+ -+ // loading was not successful -+ return false; - } - } - - /** -- * @param regionX -- * @param regionY -+ * Loads null blocks. Used when no region file is detected or an error occurs during loading. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. - */ -- public void unloadRegion(int regionX, int regionY) -+ private final void loadNullBlocks(int regionX, int regionY) - { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); -- } -- -- /** -- * @param geoX -- * @param geoY -- * @return if geodata exist -- */ -- public boolean hasGeoPos(int geoX, int geoY) -- { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // load all null blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { -- return false; -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[blockX + ix][blockY + iy] = _nullBlock; -+ } - } -- return region.hasGeo(); - } - -+ // GEODATA - GENERAL -+ - /** -- * Checks the specified position for available geodata. -- * @param x the world x -- * @param y the world y -- * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise -+ * Converts world X to geodata X. -+ * @param worldX -+ * @return int : Geo X - */ -- public boolean hasGeo(int x, int y) -+ public static int getGeoX(int worldX) - { -- return hasGeoPos(getGeoX(x), getGeoY(y)); -+ return (MathUtil.limit(worldX, World.MAP_MIN_X, World.MAP_MAX_X) - World.MAP_MIN_X) >> 4; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe check -+ * Converts world Y to geodata Y. -+ * @param worldY -+ * @return int : Geo Y - */ -- public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) -+ public static int getGeoY(int worldY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return true; -- } -- return region.checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(worldY, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.MAP_MIN_Y) >> 4; - } - - /** -+ * Converts geodata X to world X. - * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe anti-corner cut -+ * @return int : World X - */ -- public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) -+ public static int getWorldX(int geoX) - { -- boolean can = true; -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- return can && checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(geoX, 0, GeoStructure.GEO_CELLS_X) << 4) + World.MAP_MIN_X + 8; - } - - /** -- * @param geoX -+ * Converts geodata Y to world Y. - * @param geoY -- * @param worldZ -- * @return the nearest Z value -+ * @return int : World Y - */ -- public int getNearestZ(int geoX, int geoY, int worldZ) -+ public static int getWorldY(int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return worldZ; -- } -- return region.getNearestZ(geoX, geoY, worldZ); -+ return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next lower Z value -+ * Returns block of geodata on given coordinates. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return {@link ABlock} : Block of geodata. - */ -- public int getNextLowerZ(int geoX, int geoY, int worldZ) -+ private final ABlock getBlock(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final int x = geoX / GeoStructure.BLOCK_CELLS_X; -+ final int y = geoY / GeoStructure.BLOCK_CELLS_Y; -+ -+ // if x or y is out of array return null -+ if ((x > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) - { -- return worldZ; -+ return _blocks[x][y]; - } -- return region.getNextLowerZ(geoX, geoY, worldZ); -+ return null; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next higher Z value -+ * Check if geo coordinates has geo. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return boolean : True, if given geo coordinates have geodata - */ -- public int getNextHigherZ(int geoX, int geoY, int worldZ) -+ public boolean hasGeoPos(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final ABlock block = getBlock(geoX, geoY); -+ if (block == null) // null block check - { -- return worldZ; -+ // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) -+ // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); -+ return false; - } -- return region.getNextHigherZ(geoX, geoY, worldZ); -+ return block.hasGeoPos(); - } - - /** -- * Gets the Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the height of cell, which is closest to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, closest to given coordinates. - */ -- public int getHeight(int x, int y, int z) -+ public short getHeightNearest(int geoX, int geoY, int worldZ) - { -- return getNearestZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getHeightNearest(geoX, geoY, worldZ) : (short) worldZ; - } - - /** -- * Gets the next lower Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the NSWE flag byte of cell, which is closes to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. - */ -- public int getLowerHeight(int x, int y, int z) -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) - { -- return getNextLowerZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getNsweNearest(geoX, geoY, worldZ) : (byte) 0xFF; - } - - /** -- * Gets the next higher Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Check if world coordinates has geo. -+ * @param worldX : World X -+ * @param worldY : World Y -+ * @return boolean : True, if given world coordinates have geodata - */ -- public int getHigherHeight(int x, int y, int z) -+ public boolean hasGeo(int worldX, int worldY) - { -- return getNextHigherZ(getGeoX(x), getGeoY(y), z); -+ return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); - } - - /** -- * @param worldX -- * @return the geo X -+ * Returns closest Z coordinate according to geodata. -+ * @param worldX : world x -+ * @param worldY : world y -+ * @param worldZ : world z -+ * @return short : nearest Z coordinates according to geodata - */ -- public int getGeoX(int worldX) -+ public short getHeight(int worldX, int worldY, int worldZ) - { -- return (worldX - WORLD_MIN_X) / 16; -+ return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - -- /** -- * @param worldY -- * @return the geo Y -- */ -- public int getGeoY(int worldY) -- { -- return (worldY - WORLD_MIN_Y) / 16; -- } -+ // PATHFINDING - - /** -- * @param worldZ -- * @return the geo Z -+ * Check line of sight from {@link WorldObject} to {@link WorldObject}. -+ * @param origin : The origin object. -+ * @param target : The target object. -+ * @return {@code boolean} : True if origin can see target - */ -- public int getGeoZ(int worldZ) -+ public boolean canSeeTarget(WorldObject origin, WorldObject target) - { -- return (worldZ - WORLD_MIN_Z) / 16; -- } -- -- /** -- * @param geoX -- * @return the world X -- */ -- public int getWorldX(int geoX) -- { -- return (geoX * 16) + WORLD_MIN_X + 8; -- } -- -- /** -- * @param geoY -- * @return the world Y -- */ -- public int getWorldY(int geoY) -- { -- return (geoY * 16) + WORLD_MIN_Y + 8; -- } -- -- /** -- * @param geoZ -- * @return the world Z -- */ -- public int getWorldZ(int geoZ) -- { -- return (geoZ * 16) + WORLD_MIN_Z + 8; -- } -- -- /** -- * Can see target. Doors as target always return true. Checks doors between. -- * @param cha the character -- * @param target the target -- * @return {@code true} if the character can see the target (LOS), {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, WorldObject target) -- { -- if (target.isDoor()) -+ if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) - { -- // Can always see doors. - return true; - } -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param cha the character -- * @param worldPosition the world position -- * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) -- { -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param world -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @param tworld the target's instanceId -- * @return -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) -- { -- return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instance -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) -- { -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) -+ -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = target.getX(); -+ final int ty = target.getY(); -+ final int tz = target.getZ(); -+ -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } -- return canSeeTarget(x, y, z, tx, ty, tz); -- } -- -- /** -- * @param prevX -- * @param prevY -- * @param prevGeoZ -- * @param curX -- * @param curY -- * @param nswe -- * @return the LOS Z value -- */ -- private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) -- { -- if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { -- throw new RuntimeException("Multiple directions!"); -+ return false; - } - -- if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- return getNearestZ(curX, curY, prevGeoZ); -+ return true; - } - -- return getNextHigherZ(curX, curY, prevGeoZ); -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) -+ { -+ return true; -+ } -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) -+ { -+ return goz == gtz; -+ } -+ -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) -+ { -+ oheight = ((Creature) origin).getCollisionHeight() * 2; -+ } -+ -+ double theight = 0; -+ if (target.isCreature()) -+ { -+ theight = ((Creature) target).getCollisionHeight() * 2; -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); - } - - /** -- * Can see target. Does not check doors between. -- * @param xValue the x coordinate -- * @param yValue the y coordinate -- * @param zValue the z coordinate -- * @param txValue the target's x coordinate -- * @param tyValue the target's y coordinate -- * @param tzValue the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -+ * Check line of sight from {@link WorldObject} to {@link Location}. -+ * @param origin : The origin object. -+ * @param position : The target position. -+ * @return {@code boolean} : True if object can see position - */ -- public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) -+ public boolean canSeeTarget(WorldObject origin, Location position) - { -- int x = xValue; -- int y = yValue; -- int tx = txValue; -- int ty = tyValue; -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = position.getX(); -+ final int ty = position.getY(); -+ final int tz = position.getZ(); - -- int geoX = getGeoX(x); -- int geoY = getGeoY(y); -- int tGeoX = getGeoX(tx); -- int tGeoY = getGeoY(ty); -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) -+ { -+ return false; -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) -+ { -+ return false; -+ } - -- int z = getNearestZ(geoX, geoY, zValue); -- int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- // Fastpath. -- if ((geoX == tGeoX) && (geoY == tGeoY)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- if (hasGeoPos(tGeoX, tGeoY)) -- { -- return z == tz; -- } - return true; - } - -- if (tz > z) -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) - { -- int tmp = tx; -- tx = x; -- x = tmp; -- -- tmp = ty; -- ty = y; -- y = tmp; -- -- tmp = tz; -- tz = z; -- z = tmp; -- -- tmp = tGeoX; -- tGeoX = geoX; -- geoX = tmp; -- -- tmp = tGeoY; -- tGeoY = geoY; -- geoY = tmp; -+ return goz == gtz; - } - -- final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); -- // First point is guaranteed to be available, skip it, we can always see our own position. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- final int prevZ = pointIter.z(); -- int prevGeoZ = prevZ; -- int ptIndex = 0; -- while (pointIter.next()) -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight(); -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); -+ } -+ -+ /** -+ * Simple check for origin to target visibility. -+ * @param goxValue : origin X geodata coordinate -+ * @param goyValue : origin Y geodata coordinate -+ * @param gozValue : origin Z geodata coordinate -+ * @param oheight : origin height (if instance of {@link Character}) -+ * @param gtxValue : target X geodata coordinate -+ * @param gtyValue : target Y geodata coordinate -+ * @param gtzValue : target Z geodata coordinate -+ * @param theight : target height (if instance of {@link Character}) -+ * @param instance -+ * @return {@code boolean} : True, when target can be seen. -+ */ -+ private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) -+ { -+ int goz = gozValue; -+ int gtz = gtzValue; -+ int gox = goxValue; -+ int goy = goyValue; -+ int gtx = gtxValue; -+ int gty = gtyValue; -+ -+ // get line of sight Z coordinates -+ double losoz = goz + ((oheight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ double lostz = gtz + ((theight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ -+ // get X delta and signum -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirox = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; -+ final byte dirtx = sx > 0 ? GeoStructure.CELL_FLAG_W : GeoStructure.CELL_FLAG_E; -+ -+ // get Y delta and signum -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte diroy = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ final byte dirty = sy > 0 ? GeoStructure.CELL_FLAG_N : GeoStructure.CELL_FLAG_S; -+ -+ // get Z delta -+ final int dm = Math.max(dx, dy); -+ final double dz = (lostz - losoz) / dm; -+ -+ // get direction flag for diagonal movement -+ final byte diroxy = getDirXY(dirox, diroy); -+ final byte dirtxy = getDirXY(dirtx, dirty); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte diro; -+ byte dirt; -+ -+ // initialize node values -+ int nox = gox; -+ int noy = goy; -+ int ntx = gtx; -+ int nty = gty; -+ byte nsweo = getNsweNearest(gox, goy, goz); -+ byte nswet = getNsweNearest(gtx, gty, gtz); -+ -+ // loop -+ ABlock block; -+ int index; -+ for (int i = 0; i < ((dm + 1) / 2); i++) -+ { -+ // reset direction flag -+ diro = 0; -+ dirt = 0; - -- if ((curX == prevX) && (curY == prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- continue; -+ // calculate next point XY coordinates -+ d -= dy; -+ d += dx; -+ nox += sx; -+ ntx -= sx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroxy; -+ dirt |= dirtxy; - } -+ else if (e2 > -dy) -+ { -+ // calculate next point X coordinate -+ d -= dy; -+ nox += sx; -+ ntx -= sx; -+ diro |= dirox; -+ dirt |= dirtx; -+ } -+ else if (e2 < dx) -+ { -+ // calculate next point Y coordinate -+ d += dx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroy; -+ dirt |= dirty; -+ } - -- final int beeCurZ = pointIter.z(); -- int curGeoZ = prevGeoZ; -- -- // Check if the position has geodata. -- if (hasGeoPos(curX, curY)) - { -- final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); -- curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); -- int maxHeight; -- if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) -+ // get block of the next cell -+ block = getBlock(nox, noy); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nsweo & diro) == 0) - { -- maxHeight = z + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); - } - else - { -- maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); - } - -- boolean canSeeThrough = false; -- if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) -+ // layer does not exist, return -+ if (index == -1) - { -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else -- { -- canSeeThrough = true; -- } -+ return false; - } - -- if (!canSeeThrough) -+ // get layer and next line of sight Z coordinate -+ goz = block.getHeight(index); -+ losoz += dz; -+ -+ // perform line of sight check, return when fails -+ if ((goz - losoz) > Config.MAX_OBSTACLE_HEIGHT) - { - return false; - } -+ -+ // get layer nswe -+ nsweo = block.getNswe(index); - } -+ { -+ // get block of the next cell -+ block = getBlock(ntx, nty); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nswet & dirt) == 0) -+ { -+ index = block.getIndexAbove(ntx, nty, gtz - GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ else -+ { -+ index = block.getIndexBelow(ntx, nty, gtz + GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ -+ // layer does not exist, return -+ if (index == -1) -+ { -+ return false; -+ } -+ -+ // get layer and next line of sight Z coordinate -+ gtz = block.getHeight(index); -+ lostz -= dz; -+ -+ // perform line of sight check, return when fails -+ if ((gtz - lostz) > Config.MAX_OBSTACLE_HEIGHT) -+ { -+ return false; -+ } -+ -+ // get layer nswe -+ nswet = block.getNswe(index); -+ } - -- prevX = curX; -- prevY = curY; -- prevGeoZ = curGeoZ; -- ++ptIndex; -+ // update coords -+ gox = nox; -+ goy = noy; -+ gtx = ntx; -+ gty = nty; - } - -- return true; -+ // when iteration is completed, compare final Z coordinates -+ return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); - } - - /** -- * Move check. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param zValue the z coordinate -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tzValue the target's z coordinate -- * @param instance the instance -- * @return the last Location (x,y,z) where player can walk - just before wall -+ * Check movement from coordinates to coordinates. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {code boolean} : True if target coordinates are reachable from origin coordinates - */ -- public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) -+ public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- final int geoX = getGeoX(x); -- final int geoY = getGeoY(y); -- final int z = getNearestZ(geoX, geoY, zValue); -- final int tGeoX = getGeoX(tx); -- final int tGeoY = getGeoY(ty); -- final int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } -- if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = z; -+ // perform geodata check -+ final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); -+ return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); -+ } -+ -+ /** -+ * Check movement from origin to target. Returns last available point in the checked path. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {@link Location} : Last point where object can walk (just before wall) -+ */ -+ public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) -+ { -+ return new Location(ox, oy, oz); -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) -+ { -+ return new Location(ox, oy, oz); -+ } - -- while (pointIter.next()) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -- -- if (hasGeoPos(prevX, prevY)) -- { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -- { -- // Can't move, return previous location. -- return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); -- } -- } -- -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return new Location(tx, ty, tz); - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != tz)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- // Different floors, return start location. -- return new Location(x, y, z); -+ return new Location(tx, ty, tz); - } - -- return new Location(tx, ty, tz); -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) -+ { -+ return new Location(tx, ty, tz); -+ } -+ -+ // perform geodata check -+ return checkMove(gox, goy, goz, gtx, gty, gtz, instance); - } - - /** -- * Checks if its possible to move from one location to another. -- * @param fromX the X coordinate to start checking from -- * @param fromY the Y coordinate to start checking from -- * @param fromZvalue the Z coordinate to start checking from -- * @param toX the X coordinate to end checking at -- * @param toY the Y coordinate to end checking at -- * @param toZvalue the Z coordinate to end checking at -- * @param instance the instance -- * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise -+ * With this method you can check if a position is visible or can be reached by beeline movement.
-+ * Target X and Y reachable and Z is on same floor: -+ *
    -+ *
  • Location of the target with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y reachable but Z is on another floor: -+ *
    -+ *
  • Location of the origin with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y not reachable: -+ *
    -+ *
  • Last accessible location in destination to target.
  • -+ *
-+ * @param gox : origin X geodata coordinate -+ * @param goy : origin Y geodata coordinate -+ * @param goz : origin Z geodata coordinate -+ * @param gtx : target X geodata coordinate -+ * @param gty : target Y geodata coordinate -+ * @param gtz : target Z geodata coordinate -+ * @param instance -+ * @return {@link GeoLocation} : The last allowed point of movement. - */ -- public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) -+ protected final GeoLocation checkMove(int gox, int goy, int goz, int gtx, int gty, int gtz, Instance instance) - { -- final int geoX = getGeoX(fromX); -- final int geoY = getGeoY(fromY); -- final int fromZ = getNearestZ(geoX, geoY, fromZvalue); -- final int tGeoX = getGeoX(toX); -- final int tGeoY = getGeoY(toY); -- final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); -- -- if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) -+ if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } -- if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) -+ if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = fromZ; -+ // get X delta, signum and direction flag -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirX = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; - -- while (pointIter.next()) -+ // get Y delta, signum and direction flag -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte dirY = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ -+ // get direction flag for diagonal movement -+ final byte dirXY = getDirXY(dirX, dirY); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte direction; -+ -+ // load pointer coordinates -+ int gpx = gox; -+ int gpy = goy; -+ int gpz = goz; -+ -+ // load next pointer -+ int nx = gpx; -+ int ny = gpy; -+ -+ // loop -+ int count = 0; -+ while (count++ < Config.MAX_ITERATIONS) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -+ direction = 0; - -- if (hasGeoPos(prevX, prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -+ d -= dy; -+ d += dx; -+ nx += sx; -+ ny += sy; -+ direction |= dirXY; -+ } -+ else if (e2 > -dy) -+ { -+ d -= dy; -+ nx += sx; -+ direction |= dirX; -+ } -+ else if (e2 < dx) -+ { -+ d += dx; -+ ny += sy; -+ direction |= dirY; -+ } -+ -+ // obstacle found, return -+ if ((getNsweNearest(gpx, gpy, gpz) & direction) == 0) -+ { -+ return new GeoLocation(gpx, gpy, gpz); -+ } -+ -+ // update pointer coordinates -+ gpx = nx; -+ gpy = ny; -+ gpz = getHeightNearest(nx, ny, gpz); -+ -+ // target coordinates reached -+ if ((gpx == gtx) && (gpy == gty)) -+ { -+ if (gpz == gtz) - { -- return false; -+ // path found, Z coordinates are okay, return target point -+ return new GeoLocation(gtx, gty, gtz); - } -+ -+ // path found, Z coordinates are not okay, return last good point -+ return new GeoLocation(gpx, gpy, gpz); - } -+ } -+ -+ return new GeoLocation(gox, goy, goz); -+ } -+ -+ /** -+ * Returns diagonal NSWE flag format of combined two NSWE flags. -+ * @param dirX : X direction NSWE flag -+ * @param dirY : Y direction NSWE flag -+ * @return byte : NSWE flag of combined direction -+ */ -+ private static byte getDirXY(byte dirX, byte dirY) -+ { -+ // check axis directions -+ if (dirY == GeoStructure.CELL_FLAG_N) -+ { -+ if (dirX == GeoStructure.CELL_FLAG_W) -+ { -+ return GeoStructure.CELL_FLAG_NW; -+ } - -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return GeoStructure.CELL_FLAG_NE; - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) -+ if (dirX == GeoStructure.CELL_FLAG_W) - { -- // Different floors. -- return false; -+ return GeoStructure.CELL_FLAG_SW; - } - -- return true; -+ return GeoStructure.CELL_FLAG_SE; - } - -+ /** -+ * Returns the list of location objects as a result of complete path calculation. -+ * @param ox : origin x -+ * @param oy : origin y -+ * @param oz : origin z -+ * @param tx : target x -+ * @param ty : target y -+ * @param tz : target z -+ * @param instance -+ * @return {@code List} : complete path from nodes -+ */ -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ return null; -+ } -+ -+ /** -+ * Returns the instance of the {@link GeoEngine}. -+ * @return {@link GeoEngine} : The instance. -+ */ - public static GeoEngine getInstance() - { - return SingletonHolder.INSTANCE; -@@ -743,6 +934,6 @@ - - private static class SingletonHolder - { -- protected static final GeoEngine INSTANCE = new GeoEngine(); -+ protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); - } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (working copy) -@@ -20,88 +20,84 @@ - import java.util.LinkedList; - import java.util.List; - import java.util.ListIterator; --import java.util.logging.Level; --import java.util.logging.Logger; - - import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; --import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; --import org.l2jmobius.gameserver.model.World; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -+import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.instancezone.Instance; - - /** -- * @author -Nemesiss- -+ * @author Hasha - */ --public class GeoEnginePathfinding -+final class GeoEnginePathfinding extends GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); -+ // pre-allocated buffers -+ private final BufferHolder[] _buffers; - -- private BufferInfo[] _buffers; -- - protected GeoEnginePathfinding() - { -- try -+ super(); -+ -+ final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ _buffers = new BufferHolder[array.length]; -+ int count = 0; -+ for (int i = 0; i < array.length; i++) - { -- final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ final String buf = array[i]; -+ final String[] args = buf.split("x"); - -- _buffers = new BufferInfo[array.length]; -- -- String buf; -- String[] args; -- for (int i = 0; i < array.length; i++) -+ try - { -- buf = array[i]; -- args = buf.split("x"); -- if (args.length != 2) -- { -- throw new Exception("Invalid buffer definition: " + buf); -- } -- -- _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); -+ final int size = Integer.parseInt(args[1]); -+ count += size; -+ _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); - } -+ catch (Exception e) -+ { -+ LOGGER.warning("GeoEnginePathfinding: Can not load buffer setting: " + buf); -+ } - } -- catch (Exception e) -- { -- LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); -- throw new Error("CellPathFinding: load aborted"); -- } -+ -+ LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); - } - -- public boolean pathNodesExist(short regionoffset) -+ @Override -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- return false; -- } -- -- public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) -- { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -- if (!GeoEngine.getInstance().hasGeo(x, y)) -+ // get origin and check existing geo coords -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { - 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)) -+ -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coords -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { - 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)))); -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // Prepare buffer for pathfinding calculations -+ final NodeBuffer buffer = getBuffer(64 + (2 * Math.max(Math.abs(gox - gtx), Math.abs(goy - gty)))); - if (buffer == null) - { - return null; - } - -- List path = null; -+ // find path -+ List path = null; - try - { -- final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); -- -+ final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); - if (result == null) - { - return null; -@@ -125,33 +121,45 @@ - return path; - } - -- int currentX, currentY, currentZ; -- ListIterator middlePoint; -+ // get path list iterator -+ final ListIterator point = path.listIterator(); - -- middlePoint = path.listIterator(); -- currentX = x; -- currentY = y; -- currentZ = z; -+ // get node A (origin) -+ int nodeAx = gox; -+ int nodeAy = goy; -+ short nodeAz = goz; - -- while (middlePoint.hasNext()) -+ // get node B -+ GeoLocation nodeB = (GeoLocation) point.next(); -+ -+ // iterate thought the path to optimize it -+ int count = 0; -+ while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) - { -- final AbstractNodeLoc locMiddle = middlePoint.next(); -- if (!middlePoint.hasNext()) -- { -- break; -- } -+ // get node C -+ final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - -- final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); -- if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) -+ // check movement from node A to node C -+ final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); -+ if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) - { -- middlePoint.remove(); -+ // can move from node A to node C -+ -+ // remove node B -+ point.remove(); - } - else - { -- currentX = locMiddle.getX(); -- currentY = locMiddle.getY(); -- currentZ = locMiddle.getZ(); -+ // can not move from node A to node C -+ -+ // set node A (node B is part of path, update A coordinates) -+ nodeAx = nodeB.getGeoX(); -+ nodeAy = nodeB.getGeoY(); -+ nodeAz = (short) nodeB.getZ(); - } -+ -+ // set node B -+ nodeB = (GeoLocation) point.next(); - } - - return path; -@@ -158,144 +166,102 @@ - } - - /** -- * Convert geodata position to pathnode position -- * @param geo_pos -- * @return pathnode position -+ * Create list of node locations as result of calculated buffer node tree. -+ * @param node : the entry point -+ * @return List : list of node location - */ -- public short getNodePos(int geo_pos) -+ private static List constructPath(Node node) - { -- return (short) (geo_pos >> 3); // OK? -- } -- -- /** -- * Convert node position to pathnode block position -- * @param node_pos -- * @return pathnode block position (0...255) -- */ -- public short getNodeBlock(int node_pos) -- { -- return (short) (node_pos % 256); -- } -- -- public byte getRegionX(int node_pos) -- { -- return (byte) ((node_pos >> 8) + World.TILE_X_MIN); -- } -- -- public byte getRegionY(int node_pos) -- { -- return (byte) ((node_pos >> 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 node_x rx -- * @return -- */ -- public int calculateWorldX(short node_x) -- { -- return World.MAP_MIN_X + (node_x * 128) + 48; -- } -- -- /** -- * Convert pathnode y to World y position -- * @param node_y -- * @return -- */ -- public int calculateWorldY(short node_y) -- { -- return World.MAP_MIN_Y + (node_y * 128) + 48; -- } -- -- private List constructPath(AbstractNode nodeValue) -- { -- final LinkedList path = new LinkedList<>(); -- int previousDirectionX = Integer.MIN_VALUE; -- int previousDirectionY = Integer.MIN_VALUE; -- int directionX, directionY; -+ // create empty list -+ final LinkedList list = new LinkedList<>(); - -- AbstractNode node = nodeValue; -- while (node.getParent() != null) -+ // set direction X/Y -+ int dx = 0; -+ int dy = 0; -+ -+ // get target parent -+ Node target = node; -+ Node parent = target.getParent(); -+ -+ // while parent exists -+ int count = 0; -+ while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { -- directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); -- directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); -+ // get parent <> target direction X/Y -+ final int nx = parent.getLoc().getGeoX() - target.getLoc().getGeoX(); -+ final int ny = parent.getLoc().getGeoY() - target.getLoc().getGeoY(); - -- // only add a new route point if moving direction changes -- if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) -+ // direction has changed? -+ if ((dx != nx) || (dy != ny)) - { -- previousDirectionX = directionX; -- previousDirectionY = directionY; -+ // add node to the beginning of the list -+ list.addFirst(target.getLoc()); - -- path.addFirst(node.getLoc()); -- node.setLoc(null); -+ // update direction X/Y -+ dx = nx; -+ dy = ny; - } - -- node = node.getParent(); -+ // move to next node, set target and get its parent -+ target = parent; -+ parent = target.getParent(); - } - -- return path; -+ // return list -+ return list; - } - -- private CellNodeBuffer alloc(int size) -+ /** -+ * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer. -+ * @param size : pre-calculated minimal required size -+ * @return NodeBuffer : buffer -+ */ -+ private final NodeBuffer getBuffer(int size) - { -- CellNodeBuffer current = null; -- for (BufferInfo i : _buffers) -+ NodeBuffer current = null; -+ for (BufferHolder holder : _buffers) - { -- if (i.mapSize >= size) -+ // Find proper size of buffer -+ if (holder._size < size) - { -- for (CellNodeBuffer buf : i.buffer) -+ continue; -+ } -+ -+ // Find unlocked NodeBuffer -+ for (NodeBuffer buffer : holder._buffer) -+ { -+ if (!buffer.isLocked()) - { -- if (buf.lock()) -- { -- current = buf; -- break; -- } -+ continue; - } -- if (current != null) -- { -- break; -- } - -- // not found, allocate temporary buffer -- current = new CellNodeBuffer(i.mapSize); -- current.lock(); -- if (i.buffer.size() < i.count) -- { -- i.buffer.add(current); -- break; -- } -+ return buffer; - } -+ -+ // NodeBuffer not found, allocate temporary buffer -+ current = new NodeBuffer(holder._size); -+ current.isLocked(); - } - - return current; - } - -- private static final class BufferInfo -+ /** -+ * NodeBuffer container with specified size and count of separate buffers. -+ */ -+ private static final class BufferHolder - { -- final int mapSize; -- final int count; -- ArrayList buffer; -+ final int _size; -+ List _buffer; - -- public BufferInfo(int size, int cnt) -+ public BufferHolder(int size, int count) - { -- mapSize = size; -- count = cnt; -- buffer = new ArrayList<>(count); -+ _size = size; -+ _buffer = new ArrayList<>(count); -+ for (int i = 0; i < count; i++) -+ { -+ _buffer.add(new NodeBuffer(size)); -+ } - } - } -- -- public static GeoEnginePathfinding getInstance() -- { -- return SingletonHolder.INSTANCE; -- } -- -- private static class SingletonHolder -- { -- protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); -- } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (working copy) -@@ -0,0 +1,197 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @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 height of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getHeightNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first above given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, above given coordinates. -+ */ -+ public abstract short getHeightAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first below given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is closest to given coordinates. -+ * @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 the NSWE flag byte of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getNsweNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first above given coordinates. -+ * @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 getNsweAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first below given coordinates. -+ * @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 getNsweBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is closes layer to given coordinates. -+ * @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. -+ * @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 above given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is first layer below given coordinates. -+ * @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 index to data of the cell, which is first layer below given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexBelowOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeight(int index); -+ -+ /** -+ * Returns the height of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightOriginal(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNswe(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNsweOriginal(int index); -+ -+ /** -+ * Sets the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @param nswe : New NSWE flag byte. -+ */ -+ public abstract void setNswe(int index, byte nswe); -+ -+ /** -+ * Saves the block in L2D format to {@link BufferedOutputStream}. Used only for L2D geodata conversion. -+ * @param stream : The stream. -+ * @throws IOException : Can't save the block to steam. -+ */ -+ public abstract void saveBlock(BufferedOutputStream stream) throws IOException; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (working copy) -@@ -0,0 +1,252 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockComplex(ByteBuffer bb, GeoFormat format) -+ { -+ // initialize buffer -+ _buffer = new byte[GeoStructure.BLOCK_CELLS * 3]; -+ -+ // load data -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // 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); -+ } -+ else -+ { -+ // get nswe -+ final byte nswe = bb.get(); -+ _buffer[i * 3] = nswe; -+ -+ // get height -+ final short height = bb.getShort(); -+ _buffer[(i * 3) + 1] = (byte) (height & 0x00FF); -+ _buffer[(i * 3) + 2] = (byte) (height >> 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height > worldZ ? height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height < worldZ ? height : Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public 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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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 int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_COMPLEX_L2D); -+ -+ // write block data -+ stream.write(_buffer, 0, GeoStructure.BLOCK_CELLS * 3); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (working copy) -@@ -0,0 +1,176 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockFlat extends ABlock -+{ -+ protected final short _height; -+ protected byte _nswe; -+ -+ /** -+ * Creates FlatBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockFlat(ByteBuffer bb, GeoFormat format) -+ { -+ _height = bb.getShort(); -+ _nswe = format != GeoFormat.L2D ? 0x0F : (byte) (0xFF); -+ if (format == GeoFormat.L2OFF) -+ { -+ bb.getShort(); -+ } -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return true; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height > worldZ ? _height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height < worldZ ? _height : Short.MAX_VALUE; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height > worldZ ? _nswe : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height < worldZ ? _nswe : 0; -+ } -+ -+ @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return index -+ return _height < worldZ ? 0 : -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _nswe = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_FLAT_L2D); -+ -+ // write height -+ stream.write((byte) (_height & 0x00FF)); -+ stream.write((byte) (_height >> 8)); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (working copy) -@@ -0,0 +1,465 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+import java.nio.ByteOrder; -+import java.util.Arrays; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockMultilayer extends ABlock -+{ -+ private static final int MAX_LAYERS = Byte.MAX_VALUE; -+ -+ private static ByteBuffer _temp; -+ -+ /** -+ * Initializes the temporarily buffer. -+ */ -+ public static void initialize() -+ { -+ // initialize temporarily buffer and sorting mechanism -+ _temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3); -+ _temp.order(ByteOrder.LITTLE_ENDIAN); -+ } -+ -+ /** -+ * Releases temporarily buffer. -+ */ -+ public static void release() -+ { -+ _temp = null; -+ } -+ -+ protected byte[] _buffer; -+ -+ /** -+ * Implicit constructor for children class. -+ */ -+ protected BlockMultilayer() -+ { -+ _buffer = null; -+ } -+ -+ /** -+ * Creates MultilayerBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockMultilayer(ByteBuffer bb, GeoFormat format) -+ { -+ // 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 = format != GeoFormat.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++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // get data -+ final short data = bb.getShort(); -+ -+ // add nswe and height -+ _temp.put((byte) (data & 0x000F)); -+ _temp.putShort((short) ((short) (data & 0xFFF0) >> 1)); -+ } -+ else -+ { -+ // add nswe -+ _temp.put(bb.get()); -+ -+ // add height -+ _temp.putShort(bb.getShort()); -+ } -+ } -+ } -+ -+ // 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 height -+ if (height > worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, return minimum value -+ return Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 height -+ if (height < worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, return maximum value -+ return Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 nswe -+ if (height > worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 nswe -+ if (height < worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @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 first layer data (first from bottom) -+ byte layers = _buffer[index++]; -+ -+ // loop though all cell layers, find closest layer -+ 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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ // get height -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(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]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ // get nswe -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ // set nswe -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_MULTILAYER_L2D); -+ -+ // for each cell -+ int index = 0; -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ // write layers count -+ final byte layers = _buffer[index++]; -+ stream.write(layers); -+ -+ // write cell data -+ stream.write(_buffer, index, layers * 3); -+ -+ // move index to next cell -+ index += layers * 3; -+ } -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (working copy) -@@ -0,0 +1,150 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockNull extends ABlock -+{ -+ private final byte _nswe; -+ -+ public BlockNull() -+ { -+ _nswe = (byte) 0xFF; -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return false; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweBelow(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) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) -+ { -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (nonexistent) -@@ -1,48 +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 . -- */ --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() -- { -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (nonexistent) -@@ -1,77 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (nonexistent) -@@ -1,56 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (working copy) -@@ -0,0 +1,39 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public enum GeoFormat -+{ -+ L2J("%d_%d.l2j"), -+ L2OFF("%d_%d_conv.dat"), -+ L2D("%d_%d.l2d"); -+ -+ private final String _filename; -+ -+ private GeoFormat(String filename) -+ { -+ _filename = filename; -+ } -+ -+ public String getFilename() -+ { -+ return _filename; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (working copy) -@@ -0,0 +1,67 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.geoengine.GeoEngine; -+import org.l2jmobius.gameserver.model.Location; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoLocation extends Location -+{ -+ private byte _nswe; -+ -+ public GeoLocation(int x, int y, int z) -+ { -+ super(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public void set(int x, int y, short z) -+ { -+ super.setXYZ(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public int getGeoX() -+ { -+ return _x; -+ } -+ -+ public int getGeoY() -+ { -+ return _y; -+ } -+ -+ @Override -+ public int getX() -+ { -+ return GeoEngine.getWorldX(_x); -+ } -+ -+ @Override -+ public int getY() -+ { -+ return GeoEngine.getWorldY(_y); -+ } -+ -+ public byte getNSWE() -+ { -+ return _nswe; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (working copy) -@@ -0,0 +1,70 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoStructure -+{ -+ // cells -+ public static final byte CELL_FLAG_E = 1 << 0; -+ public static final byte CELL_FLAG_W = 1 << 1; -+ public static final byte CELL_FLAG_S = 1 << 2; -+ public static final byte CELL_FLAG_N = 1 << 3; -+ public static final byte CELL_FLAG_SE = 1 << 4; -+ public static final byte CELL_FLAG_SW = 1 << 5; -+ public static final byte CELL_FLAG_NE = 1 << 6; -+ public static final byte CELL_FLAG_NW = (byte) (1 << 7); -+ -+ public static final int CELL_HEIGHT = 8; -+ public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6; -+ -+ // blocks -+ public static final byte TYPE_FLAT_L2J_L2OFF = 0; -+ public static final byte TYPE_FLAT_L2D = (byte) 0xD0; -+ public static final byte TYPE_COMPLEX_L2J = 1; -+ public static final byte TYPE_COMPLEX_L2OFF = 0x40; -+ public static final byte TYPE_COMPLEX_L2D = (byte) 0xD1; -+ 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) -+ public static final byte TYPE_MULTILAYER_L2D = (byte) 0xD2; -+ -+ 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; -+ -+ // regions -+ 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; -+ -+ // global geodata -+ private static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); -+ private 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; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (nonexistent) -@@ -1,42 +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 . -- */ --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); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (working copy) -@@ -0,0 +1,53 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public interface IGeoObject -+{ -+ /** -+ * Returns geodata X coordinate of the {@link IGeoObject}. -+ * @return int : Geodata X coordinate. -+ */ -+ int getGeoX(); -+ -+ /** -+ * Returns geodata Y coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Y coordinate. -+ */ -+ int getGeoY(); -+ -+ /** -+ * Returns geodata Z coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Z coordinate. -+ */ -+ int getGeoZ(); -+ -+ /** -+ * Returns height of the {@link IGeoObject}. -+ * @return int : Height. -+ */ -+ int getHeight(); -+ -+ /** -+ * Returns {@link IGeoObject} data. -+ * @return byte[][] : {@link IGeoObject} data. -+ */ -+ byte[][] getObjectGeoData(); -+} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (nonexistent) -@@ -1,47 +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 . -- */ --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(); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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); -- -- // 1 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (nonexistent) -@@ -1,55 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --/** -- * @author HorridoJoho -- */ --public final class NullRegion implements IRegion --{ -- public static final NullRegion INSTANCE = new NullRegion(); -- -- @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 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Region.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (nonexistent) -@@ -1,92 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (nonexistent) -@@ -1,87 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public abstract class AbstractNode --{ -- private T _loc; -- private AbstractNode _parent; -- -- public AbstractNode(T loc) -- { -- _loc = loc; -- } -- -- public void setParent(AbstractNode p) -- { -- _parent = p; -- } -- -- public AbstractNode getParent() -- { -- return _parent; -- } -- -- public T getLoc() -- { -- return _loc; -- } -- -- public void setLoc(T l) -- { -- _loc = l; -- } -- -- @Override -- public int hashCode() -- { -- final int prime = 31; -- int result = 1; -- result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); -- return result; -- } -- -- @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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (nonexistent) -@@ -1,33 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --/** -- * @author -Nemesiss- -- */ --public abstract class AbstractNodeLoc --{ -- public abstract int getX(); -- -- public abstract int getY(); -- -- public abstract int getZ(); -- -- public abstract int getNodeX(); -- -- public abstract int getNodeY(); --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (nonexistent) -@@ -1,67 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public class CellNode extends AbstractNode --{ -- 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (nonexistent) -@@ -1,348 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --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 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) -- { -- _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(); -- } -- -- public final List debugPath() -- { -- final List 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 void getNeighbors() -- { -- if (!_current.getLoc().canGoAll()) -- { -- 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); -- } -- -- // SouthEast -- if ((nodeE != null) && (nodeS != null)) -- { -- if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) -- { -- addNode(x + 1, y + 1, z, true); -- } -- } -- -- // SouthWest -- if ((nodeS != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) -- { -- addNode(x - 1, y + 1, z, true); -- } -- } -- -- // NorthEast -- if ((nodeN != null) && (nodeE != null)) -- { -- if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) -- { -- addNode(x + 1, y - 1, z, true); -- } -- } -- -- // NorthWest -- if ((nodeN != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) -- { -- addNode(x - 1, y - 1, z, true); -- } -- } -- } -- -- private 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(); -- // reinit node if needed -- if (result.getLoc() != null) -- { -- result.getLoc().set(x, y, z); -- } -- else -- { -- result.setLoc(new NodeLoc(x, y, z)); -- } -- } -- -- return result; -- } -- -- private 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 boolean isHighWeight(int x, int y, int z) -- { -- final CellNode result = getNode(x, y, z); -- if (result == null) -- { -- return true; -- } -- -- if (!result.getLoc().canGoAll()) -- { -- return true; -- } -- if (Math.abs(result.getLoc().getZ() - z) > 16) -- { -- return true; -- } -- -- return false; -- } -- -- private 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (working copy) -@@ -0,0 +1,87 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+ -+/** -+ * @author Hasha -+ */ -+public class Node -+{ -+ // node coords and nswe flag -+ private GeoLocation _loc; -+ -+ // node parent (for reverse path construction) -+ private Node _parent; -+ // node child (for moving over nodes during iteration) -+ private Node _child; -+ -+ // node G cost (movement cost = parent movement cost + current movement cost) -+ private double _cost = -1000; -+ -+ public void setLoc(int x, int y, int z) -+ { -+ _loc = new GeoLocation(x, y, z); -+ } -+ -+ public GeoLocation getLoc() -+ { -+ return _loc; -+ } -+ -+ public void setParent(Node parent) -+ { -+ _parent = parent; -+ } -+ -+ public Node getParent() -+ { -+ return _parent; -+ } -+ -+ public void setChild(Node child) -+ { -+ _child = child; -+ } -+ -+ public Node getChild() -+ { -+ return _child; -+ } -+ -+ public void setCost(double cost) -+ { -+ _cost = cost; -+ } -+ -+ public double getCost() -+ { -+ return _cost; -+ } -+ -+ public void free() -+ { -+ // reset node location -+ _loc = null; -+ -+ // reset node parent, child and cost -+ _parent = null; -+ _child = null; -+ _cost = -1000; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (working copy) -@@ -0,0 +1,306 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import java.util.concurrent.locks.ReentrantLock; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+ -+/** -+ * @author DS, Hasha; Credits to Diamond -+ */ -+public class NodeBuffer -+{ -+ private final ReentrantLock _lock = new ReentrantLock(); -+ private final int _size; -+ private final Node[][] _buffer; -+ -+ // center coordinates -+ private int _cx = 0; -+ private int _cy = 0; -+ -+ // target coordinates -+ private int _gtx = 0; -+ private int _gty = 0; -+ private short _gtz = 0; -+ -+ private Node _current = null; -+ -+ /** -+ * Constructor of NodeBuffer. -+ * @param size : one dimension size of buffer -+ */ -+ public NodeBuffer(int size) -+ { -+ // set size -+ _size = size; -+ -+ // initialize buffer -+ _buffer = new Node[size][size]; -+ for (int x = 0; x < size; x++) -+ { -+ for (int y = 0; y < size; y++) -+ { -+ _buffer[x][y] = 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 Node : first node of path -+ */ -+ public Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) -+ { -+ // set coordinates (middle of the line (gox,goy) - (gtx,gty), will be in the center of the buffer) -+ _cx = gox + ((gtx - gox - _size) / 2); -+ _cy = goy + ((gty - goy - _size) / 2); -+ -+ _gtx = gtx; -+ _gty = gty; -+ _gtz = gtz; -+ -+ _current = getNode(gox, goy, goz); -+ _current.setCost(getCostH(gox, goy, goz)); -+ -+ int count = 0; -+ do -+ { -+ // reached target? -+ if ((_current.getLoc().getGeoX() == _gtx) && (_current.getLoc().getGeoY() == _gty) && (Math.abs(_current.getLoc().getZ() - _gtz) < 8)) -+ { -+ return _current; -+ } -+ -+ // expand current node -+ expand(); -+ -+ // move pointer -+ _current = _current.getChild(); -+ } -+ while ((_current != null) && (count++ < Config.MAX_ITERATIONS)); -+ -+ return null; -+ } -+ -+ public boolean isLocked() -+ { -+ return _lock.tryLock(); -+ } -+ -+ public void free() -+ { -+ _current = null; -+ -+ for (Node[] nodes : _buffer) -+ { -+ for (Node node : nodes) -+ { -+ if (node.getLoc() != null) -+ { -+ node.free(); -+ } -+ } -+ } -+ -+ _lock.unlock(); -+ } -+ -+ /** -+ * Check _current Node and add its neighbors to the buffer. -+ */ -+ private final void expand() -+ { -+ // can't move anywhere, don't expand -+ final byte nswe = _current.getLoc().getNSWE(); -+ if (nswe == 0) -+ { -+ return; -+ } -+ -+ // get geo coords of the node to be expanded -+ final int x = _current.getLoc().getGeoX(); -+ final int y = _current.getLoc().getGeoY(); -+ final short z = (short) _current.getLoc().getZ(); -+ -+ // can move north, expand -+ if ((nswe & GeoStructure.CELL_FLAG_N) != 0) -+ { -+ addNode(x, y - 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move south, expand -+ if ((nswe & GeoStructure.CELL_FLAG_S) != 0) -+ { -+ addNode(x, y + 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_W) != 0) -+ { -+ addNode(x - 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_E) != 0) -+ { -+ addNode(x + 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move north-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NW) != 0) -+ { -+ addNode(x - 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move north-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NE) != 0) -+ { -+ addNode(x + 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SW) != 0) -+ { -+ addNode(x - 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SE) != 0) -+ { -+ addNode(x + 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ } -+ -+ /** -+ * Returns node, if it exists in buffer. -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param z : node Z coord -+ * @return Node : node, if exits in buffer -+ */ -+ private final Node getNode(int x, int y, short z) -+ { -+ // check node X out of coordinates -+ final int ix = x - _cx; -+ if ((ix < 0) || (ix >= _size)) -+ { -+ return null; -+ } -+ -+ // check node Y out of coordinates -+ final int iy = y - _cy; -+ if ((iy < 0) || (iy >= _size)) -+ { -+ return null; -+ } -+ -+ // get node -+ final Node result = _buffer[ix][iy]; -+ -+ // check and update -+ if (result.getLoc() == null) -+ { -+ result.setLoc(x, y, z); -+ } -+ -+ // return node -+ return result; -+ } -+ -+ /** -+ * Add node given by coordinates to the buffer. -+ * @param x : geo X coord -+ * @param y : geo Y coord -+ * @param z : geo Z coord -+ * @param weight : weight of movement to new node -+ */ -+ private final void addNode(int x, int y, short z, int weight) -+ { -+ // get node to be expanded -+ final Node node = getNode(x, y, z); -+ if (node == null) -+ { -+ return; -+ } -+ -+ // Z distance between nearby cells is higher than cell size -+ if (node.getLoc().getZ() > (z + (2 * GeoStructure.CELL_HEIGHT))) -+ { -+ return; -+ } -+ -+ // node was already expanded, return -+ if (node.getCost() >= 0) -+ { -+ return; -+ } -+ -+ node.setParent(_current); -+ if (node.getLoc().getNSWE() != (byte) 0xFF) -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + (weight * Config.OBSTACLE_MULTIPLIER)); -+ } -+ else -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + weight); -+ } -+ -+ Node current = _current; -+ int count = 0; -+ while ((current.getChild() != null) && (count < (Config.MAX_ITERATIONS * 4))) -+ { -+ count++; -+ if (current.getChild().getCost() > node.getCost()) -+ { -+ node.setChild(current.getChild()); -+ break; -+ } -+ current = current.getChild(); -+ } -+ -+ if (count >= (Config.MAX_ITERATIONS * 4)) -+ { -+ System.err.println("Pathfinding: too long loop detected, cost:" + node.getCost()); -+ } -+ -+ current.setChild(node); -+ } -+ -+ /** -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param i : node Z coord -+ * @return double : node cost -+ */ -+ private final double getCostH(int x, int y, int i) -+ { -+ final int dX = x - _gtx; -+ final int dY = y - _gty; -+ final int dZ = (i - _gtz) / GeoStructure.CELL_HEIGHT; -+ -+ // return (Math.abs(dX) + Math.abs(dY) + Math.abs(dZ)) * Config.HEURISTIC_WEIGHT; // Manhattan distance -+ return Math.sqrt((dX * dX) + (dY * dY) + (dZ * dZ)) * Config.HEURISTIC_WEIGHT; // Direct distance -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -- --/** -- * @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 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 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; -- // return super.hashCode(); -- } -- -- @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; -- if (_x != other._x) -- { -- return false; -- } -- if (_y != other._y) -- { -- return false; -- } -- if (_goNorth != other._goNorth) -- { -- return false; -- } -- if (_goEast != other._goEast) -- { -- return false; -- } -- if (_goSouth != other._goSouth) -- { -- return false; -- } -- if (_goWest != other._goWest) -- { -- return false; -- } -- if (_geoHeight != other._geoHeight) -- { -- return false; -- } -- return true; -- } --} -Index: java/org/l2jmobius/gameserver/model/actor/Creature.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/Creature.java (revision 8428) -+++ java/org/l2jmobius/gameserver/model/actor/Creature.java (working copy) -@@ -65,8 +65,6 @@ - import org.l2jmobius.gameserver.enums.TeleportWhereType; - import org.l2jmobius.gameserver.enums.UserInfoType; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; - import org.l2jmobius.gameserver.instancemanager.IdManager; - import org.l2jmobius.gameserver.instancemanager.MapRegionManager; - import org.l2jmobius.gameserver.instancemanager.QuestManager; -@@ -2566,7 +2564,7 @@ - - public boolean disregardingGeodata; - public int onGeodataPathIndex; -- public List geoPath; -+ public List geoPath; - public int geoPathAccurateTx; - public int geoPathAccurateTy; - public int geoPathGtx; -@@ -3443,7 +3441,7 @@ - if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) - { - // Path calculation -- overrides previous movement check -- m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); -+ m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); - if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found - { - if (isPlayer() && !_isFlying && !isInWater) -Index: java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (revision 8424) -+++ java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (working copy) -@@ -12746,7 +12746,7 @@ - { - if (Config.CORRECT_PLAYER_Z) - { -- final int nearestZ = GeoEngine.getInstance().getHigherHeight(getX(), getY(), getZ()); -+ final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); - if (getZ() < nearestZ) - { - teleToLocation(new Location(getX(), getY(), nearestZ)); -Index: java/org/l2jmobius/gameserver/util/GeoUtils.java -=================================================================== ---- java/org/l2jmobius/gameserver/util/GeoUtils.java (revision 8409) -+++ java/org/l2jmobius/gameserver/util/GeoUtils.java (working copy) -@@ -19,7 +19,7 @@ - import java.awt.Color; - - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; - import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; - -@@ -26,25 +26,25 @@ - /** - * @author HorridoJoho - */ --public final class GeoUtils -+public class GeoUtils - { - public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); - - final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); - - while (iter.next()) - { -- final int wx = GeoEngine.getInstance().getWorldX(iter.x()); -- final int wy = GeoEngine.getInstance().getWorldY(iter.y()); -+ final int wx = GeoEngine.getWorldX(iter.x()); -+ final int wy = GeoEngine.getWorldY(iter.y()); - - prim.addPoint(Color.RED, wx, wy, z); - } -@@ -53,21 +53,21 @@ - - public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); -- int wy = GeoEngine.getInstance().getWorldY(prevY); -+ int wx = GeoEngine.getWorldX(prevX); -+ int wy = GeoEngine.getWorldY(prevY); - int wz = iter.z(); - prim.addPoint(Color.RED, wx, wy, wz); - -@@ -78,8 +78,8 @@ - - if ((curX != prevX) || (curY != prevY)) - { -- wx = GeoEngine.getInstance().getWorldX(curX); -- wy = GeoEngine.getInstance().getWorldY(curY); -+ wx = GeoEngine.getWorldX(curX); -+ wy = GeoEngine.getWorldY(curY); - wz = iter.z(); - - prim.addPoint(Color.RED, wx, wy, wz); -@@ -93,7 +93,7 @@ - - private static Color getDirectionColor(int x, int y, int z, int nswe) - { -- if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) -+ if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) - { - return Color.GREEN; - } -@@ -109,9 +109,8 @@ - int iPacket = 0; - - ExServerPrimitive exsp = null; -- final GeoEngine ge = GeoEngine.getInstance(); -- final int playerGx = ge.getGeoX(player.getX()); -- final int playerGy = ge.getGeoY(player.getY()); -+ final int playerGx = GeoEngine.getGeoX(player.getX()); -+ final int playerGy = GeoEngine.getGeoY(player.getY()); - for (int dx = -geoRadius; dx <= geoRadius; ++dx) - { - for (int dy = -geoRadius; dy <= geoRadius; ++dy) -@@ -135,12 +134,12 @@ - final int gx = playerGx + dx; - final int gy = playerGy + dy; - -- final int x = ge.getWorldX(gx); -- final int y = ge.getWorldY(gy); -- final int z = ge.getNearestZ(gx, gy, player.getZ()); -+ final int x = GeoEngine.getWorldX(gx); -+ final int y = GeoEngine.getWorldY(gy); -+ final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); - - // north arrow -- Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); -+ Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); - 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); -@@ -147,7 +146,7 @@ - exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z); - - // east arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); - 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); -@@ -154,13 +153,13 @@ - exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z); - - // south arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); - 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, Cell.NSWE_WEST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); - 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); -@@ -188,15 +187,15 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_EAST; -+ return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_EAST; -+ return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; - } - else - { -- return Cell.NSWE_EAST; -+ return GeoStructure.CELL_FLAG_E; // Direction.EAST; - } - } - else if (x < lastX) // west -@@ -203,26 +202,27 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_WEST; -+ return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_WEST; -+ return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; - } - else - { -- return Cell.NSWE_WEST; -+ return GeoStructure.CELL_FLAG_W; // Direction.WEST; - } - } -- else // unchanged x -+ else -+ // unchanged x - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH; -+ return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH; -+ return GeoStructure.CELL_FLAG_N; // Direction.NORTH; - } - else - { -Index: java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java -=================================================================== ---- java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (nonexistent) -+++ java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (working copy) -@@ -0,0 +1,386 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.tools.geodataconverter; -+ -+import java.io.BufferedOutputStream; -+import java.io.File; -+import java.io.FileOutputStream; -+import java.io.RandomAccessFile; -+import java.nio.ByteOrder; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.Scanner; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.commons.util.PropertiesParser; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoDataConverter -+{ -+ private static GeoFormat _format; -+ private static ABlock[][] _blocks; -+ -+ public static void main(String[] args) -+ { -+ // initialize config -+ loadGeoengineConfigs(); -+ -+ // get geodata format -+ String type = ""; -+ try (Scanner scn = new Scanner(System.in)) -+ { -+ while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) -+ { -+ System.out.println("GeoDataConverter: Select source geodata type:"); -+ System.out.println(" J: L2J (e.g. 23_22.l2j)"); -+ System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); -+ System.out.println(" E: Exit"); -+ System.out.print("Choice: "); -+ type = scn.next(); -+ } -+ } -+ if (type.equalsIgnoreCase("E")) -+ { -+ System.exit(0); -+ } -+ -+ _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; -+ -+ // start conversion -+ System.out.println("GeoDataConverter: Converting all " + _format + " files."); -+ -+ // initialize geodata container -+ _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files -+ int converted = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) -+ { -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) -+ { -+ final String input = String.format(_format.getFilename(), rx, ry); -+ final String filepath = Config.GEODATA_PATH; -+ final File f = new File(filepath + input); -+ if (f.exists() && !f.isDirectory()) -+ { -+ // load geodata -+ if (!loadGeoBlocks(input)) -+ { -+ System.out.println("GeoDataConverter: Unable to load " + input + " region file."); -+ continue; -+ } -+ -+ // recalculate nswe -+ if (!recalculateNswe()) -+ { -+ System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); -+ continue; -+ } -+ -+ // save geodata -+ final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); -+ if (!saveGeoBlocks(output)) -+ { -+ System.out.println("GeoDataConverter: Unable to save " + output + " region file."); -+ continue; -+ } -+ -+ converted++; -+ System.out.println("GeoDataConverter: Created " + output + " region file."); -+ } -+ } -+ } -+ System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); -+ } -+ -+ /** -+ * Loads geo blocks from buffer of the region file. -+ * @param filename : The name of the to load. -+ * @return boolean : True when successful. -+ */ -+ private static boolean loadGeoBlocks(String filename) -+ { -+ // region file is load-able, try to load it -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) -+ { -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) -+ if (_format == GeoFormat.L2OFF) -+ { -+ for (int i = 0; i < 18; i++) -+ { -+ buffer.get(); -+ } -+ } -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ if (_format == GeoFormat.L2J) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2J: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2J: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ else -+ { -+ // get block type -+ final short type = buffer.getShort(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ default: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ if (buffer.remaining() > 0) -+ { -+ System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ return false; -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); -+ return false; -+ } -+ } -+ -+ /** -+ * Recalculate diagonal flags for the region file. -+ * @return boolean : True when successful. -+ */ -+ private static boolean recalculateNswe() -+ { -+ try -+ { -+ for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) -+ { -+ for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) -+ { -+ // get block -+ final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // skip flat blocks -+ if (block instanceof BlockFlat) -+ { -+ continue; -+ } -+ -+ // for complex and multilayer blocks go though all layers -+ short height = Short.MAX_VALUE; -+ int index; -+ while ((index = block.getIndexBelow(x, y, height)) != -1) -+ { -+ // get height and nswe -+ height = block.getHeight(index); -+ byte nswe = block.getNswe(index); -+ -+ // update nswe with diagonal flags -+ nswe = updateNsweBelow(x, y, height, nswe); -+ -+ // set nswe of the cell -+ block.setNswe(index, nswe); -+ } -+ } -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ /** -+ * Updates the NSWE flag with diagonal flags. -+ * @param x : Geodata X coordinate. -+ * @param y : Geodata Y coordinate. -+ * @param z : Geodata Z coordinate. -+ * @param nsweValue : NSWE flag to be updated. -+ * @return byte : Updated NSWE flag. -+ */ -+ private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) -+ { -+ byte nswe = nsweValue; -+ -+ // calculate virtual layer height -+ final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); -+ -+ // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) -+ final byte nsweN = getNsweBelow(x, y - 1, height); -+ final byte nsweS = getNsweBelow(x, y + 1, height); -+ final byte nsweW = getNsweBelow(x - 1, y, height); -+ final byte nsweE = getNsweBelow(x + 1, y, height); -+ -+ // north-west -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NW; -+ } -+ -+ // north-east -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NE; -+ } -+ -+ // south-west -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SW; -+ } -+ -+ // south-east -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SE; -+ } -+ -+ return nswe; -+ } -+ -+ private static byte getNsweBelow(int geoX, int geoY, short worldZ) -+ { -+ // out of geo coordinates -+ if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) -+ { -+ return 0; -+ } -+ -+ // out of geo coordinates -+ if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) -+ { -+ return 0; -+ } -+ -+ // get block -+ final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // get index, when valid, return nswe -+ final int index = block.getIndexBelow(geoX, geoY, worldZ); -+ return index == -1 ? 0 : block.getNswe(index); -+ } -+ -+ /** -+ * Save region file to file. -+ * @param filename : The name of file to save. -+ * @return boolean : True when successful. -+ */ -+ private static boolean saveGeoBlocks(String filename) -+ { -+ try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) -+ { -+ // loop over region blocks and save each block -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[ix][iy].saveBlock(bos); -+ } -+ } -+ -+ // flush data to file -+ bos.flush(); -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ private static void loadGeoengineConfigs() -+ { -+ final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); -+ Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); -+ Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); -+ Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); -+ Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); -+ Config.PATHFINDING = geoData.getBoolean("PathFinding", true); -+ Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -+ Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); -+ Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); -+ Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); -+ Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); -+ Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); -+ } -+} -\ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/geodata/Readme.txt b/L2J_Mobius_5.0_Salvation/dist/game/data/geodata/Readme.txt index a480c8c380..2611882e56 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/geodata/Readme.txt +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/geodata/Readme.txt @@ -23,8 +23,8 @@ a - Prerequisites b - Make it work ---------------------------------------------- -To make geodata working: -* unpack your geodata files into "/data/geodata" folder -* open "/config/GeoEngine.ini" with your favorite text editor and then edit following config: - - CoordSynchronize = 2 -* If you do not use any geodata files, the server will automatically change this setting to -1. +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. diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java index 037b29d579..89425b0d85 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java @@ -295,7 +295,7 @@ public class FourSepulchers extends AbstractNpcAI implements IXmlReader { if ((npc != null) && !npc.isDead()) { - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), npc.getSpawn().getLocation().getX() + getRandom(-400, 400), npc.getSpawn().getLocation().getY() + getRandom(-400, 400), npc.getZ(), npc.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), npc.getSpawn().getLocation().getX() + getRandom(-400, 400), npc.getSpawn().getLocation().getY() + getRandom(-400, 400), npc.getZ(), npc.getInstanceWorld()); if (Util.calculateDistance(npc, npc.getSpawn().getLocation(), false, false) < 600) { npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java index 7aa8789df0..00bffa39b8 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java @@ -270,7 +270,7 @@ public class PrimevalIsle extends AbstractNpcAI final double cos = Math.cos(radian); final int newX = (int) (npc.getX() + (cos * distance)); final int newY = (int) (npc.getY() + (sin * distance)); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, npc.getZ(), npc.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, npc.getZ(), npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, loc, 0); } else if (ag_type == 1) diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java index 0a77f7404d..772f5fb5d7 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java @@ -78,7 +78,7 @@ public class BoyAndGirl extends AbstractNpcAI startQuestTimer("NPC_SHOUT", 10000 + (getRandom(5) * 1000), npc, null); npc.setRunning(); final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 200, 600); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); return super.onSpawn(npc); } @@ -86,7 +86,7 @@ public class BoyAndGirl extends AbstractNpcAI public void onMoveFinished(Npc npc) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 200, 600); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); super.onMoveFinished(npc); } diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java index 558f2631e6..3bcf3810cb 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java @@ -48,7 +48,7 @@ public class Devno extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java index 7525cc99e8..ce81f5c05c 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java @@ -48,7 +48,7 @@ public class Eleve extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java index 7d9150b156..f4e942c326 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java @@ -46,7 +46,7 @@ public class Handermonkey extends AbstractNpcAI { final int x = npc.getSpawn().getX() + (getRandom(-100, 100)); final int y = npc.getSpawn().getY() + (getRandom(-100, 100)); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x, y, npc.getZ(), npc.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x, y, npc.getZ(), npc.getInstanceWorld()); addMoveToDesire(npc, loc, 0); } else diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java index c993ce8936..7df61da041 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java @@ -48,7 +48,7 @@ public class Karonf extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java index 6e8cbee920..589d376f6b 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java @@ -48,7 +48,7 @@ public class Marsha extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java index c616abccc6..5c09d531c8 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java @@ -48,7 +48,7 @@ public class Morgan extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java index 3652b4ab02..fe20952121 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java @@ -48,7 +48,7 @@ public class Rubentis extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", 10000 + (getRandom(5) * 1000), npc, null); } diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java index beece64521..b0944f763d 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java @@ -63,7 +63,7 @@ public class Vortex extends AbstractNpcAI final int x = (int) (attackers.getX() + (600 * Math.cos(radians))); final int y = (int) (attackers.getY() + (600 * Math.sin(radians))); final int z = attackers.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(attackers.getX(), attackers.getY(), attackers.getZ(), x, y, z, attackers.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(attackers.getX(), attackers.getY(), attackers.getZ(), x, y, z, attackers.getInstanceWorld()); attackers.broadcastPacket(new FlyToLocation(attackers, x, y, z, FlyToLocation.FlyType.THROW_UP, 800, 800, 800)); attackers.setXYZ(loc); attackers.broadcastPacket(new ValidateLocation(attackers)); diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/others/FleeMonsters.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/others/FleeMonsters.java index 9346aebc95..b5de804b28 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/others/FleeMonsters.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/ai/others/FleeMonsters.java @@ -68,7 +68,7 @@ public class FleeMonsters extends AbstractNpcAI final int posX = (int) (npc.getX() + (FLEE_DISTANCE * Math.cos(radians))); final int posY = (int) (npc.getY() + (FLEE_DISTANCE * Math.sin(radians))); final int posZ = npc.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, attacker.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); return super.onAttack(npc, attacker, damage, isSummon); } diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index 524652cbc1..18511c6541 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { @@ -133,8 +133,8 @@ public class AdminGeodata implements IAdminCommandHandler } case "admin_geomap": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y + " (" + ((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + "," + ((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + " to " + ((((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + "," + ((((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + ")"); break; } diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java index f3dffa1227..76572f7f25 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java @@ -57,8 +57,8 @@ public class AdminMissingHtmls implements IAdminCommandHandler { case "admin_geomap_missing_htmls": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final int topLeftX = (x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE; final int topLeftY = (y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE; final int bottomRightX = (((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1; diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index fdb2319138..a0a34f1051 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -19,9 +19,9 @@ package handlers.admincommandhandlers; import java.util.List; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; +import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.util.BuilderUtil; @@ -44,13 +44,13 @@ public class AdminPathNode implements IAdminCommandHandler } if (activeChar.getTarget() != null) { - final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { BuilderUtil.sendSysMessage(activeChar, "No Route!"); return true; } - for (AbstractNodeLoc a : path) + for (Location a : path) { BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/Blink.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/Blink.java index 22f2c84eae..22697e8e20 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/Blink.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/Blink.java @@ -88,7 +88,7 @@ public class Blink extends AbstractEffect final int y = effected.getY() + y1; final int z = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, destination, _flyType, _flySpeed, _flyDelay, _animationSpeed)); diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/Fear.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/Fear.java index 95a5afdf98..88c795e04b 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/Fear.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/Fear.java @@ -100,7 +100,7 @@ public class Fear extends AbstractEffect final int posY = (int) (effected.getY() + (FEAR_RANGE * Math.sin(radians))); final int posZ = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); } } diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java index fddd94c60a..55c2e78d8a 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java @@ -57,7 +57,7 @@ public class FlyAway extends AbstractEffect final int y = (int) (effector.getY() - (nRadius * (dy / distance))); final int z = effector.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.broadcastPacket(new FlyToLocation(effected, destination, FlyType.THROW_UP)); effected.setXYZ(destination); diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java index 3c3f9aca62..14809ac499 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java @@ -179,7 +179,7 @@ public class KnockBack extends AbstractEffect final int x = (int) (effected.getX() + (_distance * Math.cos(radians))); final int y = (int) (effected.getY() + (_distance * Math.sin(radians))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, loc, _type, _speed, _delay, _animationSpeed)); diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/PullBack.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/PullBack.java index ed6a37eff6..38fe2fe9d0 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/PullBack.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/PullBack.java @@ -73,7 +73,7 @@ public class PullBack extends AbstractEffect } // In retail, you get debuff, but you are not even moved if there is obstacle. You are still disabled from using skills and moving though. - if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effector.getInstanceWorld())) + if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effected.getInstanceWorld())) { effected.broadcastPacket(new FlyToLocation(effected, effector, _type, _speed, _delay, _animationSpeed)); effected.setXYZ(effector.getX(), effector.getY(), GeoEngine.getInstance().getHeight(effector.getX(), effector.getY(), effector.getZ()) + 10); diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java index d6f9664141..813e496c47 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java @@ -87,7 +87,7 @@ public class TeleportToSummon extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = summon.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java index d3c263978c..c9dc34ac22 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java @@ -76,7 +76,7 @@ public class TeleportToTarget extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java index f2148adb54..c7ca6f64cb 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java @@ -65,7 +65,7 @@ public class RollingDice implements IItemHandler final int x = player.getX() + x1; final int y = player.getY() + y1; final int z = player.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); Broadcast.toSelfAndKnownPlayers(player, new Dice(player.getObjectId(), itemId, number, destination.getX(), destination.getY(), destination.getZ())); final SystemMessage sm = new SystemMessage(SystemMessageId.C1_HAS_ROLLED_A_S2); diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/targethandlers/Ground.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/targethandlers/Ground.java index acce8f1b2f..1328ff01b5 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/targethandlers/Ground.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/targethandlers/Ground.java @@ -52,7 +52,7 @@ public class Ground implements ITargetTypeHandler return null; } - if (!GeoEngine.getInstance().canSeeTarget(creature, worldPosition)) + if (!GeoEngine.getInstance().canSeeLocation(creature, worldPosition)) { if (sendMessage) { diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/Config.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/Config.java index 46f17605d9..ff1dc10fa3 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/Config.java @@ -61,6 +61,7 @@ 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; @@ -937,12 +938,17 @@ public class Config // GeoEngine // -------------------------------------------------- public static Path GEODATA_PATH; + public static GeoType GEODATA_TYPE; public static boolean PATHFINDING; public static String PATHFIND_BUFFERS; - public static float LOW_WEIGHT; - public static float MEDIUM_WEIGHT; - public static float HIGH_WEIGHT; - public static float DIAGONAL_WEIGHT; + public static int MOVE_WEIGHT; + public static int MOVE_WEIGHT_DIAG; + public static int OBSTACLE_WEIGHT; + 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; /** Attribute System */ public static int S_WEAPON_STONE; @@ -2488,12 +2494,17 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); + GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); - MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); - HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); - DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); + MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); + OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); + HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); + MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); + MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); // Load AllowedPlayerRaces config file (if exists) final PropertiesParser AllowedPlayerRaces = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE); diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/commons/util/CommonUtil.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/commons/util/CommonUtil.java index 14ae3a4560..ab7837d827 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/commons/util/CommonUtil.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/commons/util/CommonUtil.java @@ -584,4 +584,15 @@ public class CommonUtil final DecimalFormat formatter = new DecimalFormat(format, new DecimalFormatSymbols(Locale.ENGLISH)); return formatter.format(value); } + + /** + * @param numToTest : The number to test. + * @param min : The minimum limit. + * @param max : The maximum limit. + * @return the number or one of the limit (mininum / maximum). + */ + public static int limit(int numToTest, int min, int max) + { + return (numToTest > max) ? max : ((numToTest < min) ? min : numToTest); + } } diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/commons/util/Point2D.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/commons/util/Point2D.java new file mode 100644 index 0000000000..ebb6272e5e --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/commons/util/Point2D.java @@ -0,0 +1,180 @@ +/* + * 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 . + */ +package org.l2jmobius.commons.util; + +import java.util.Objects; + +/** + * A datatype used to retain a 2D (x/y) point. It got the capability to be set and cleaned. + */ +public class Point2D +{ + protected volatile int _x; + protected volatile int _y; + + public Point2D(int x, int y) + { + _x = x; + _y = y; + } + + @Override + public Point2D clone() + { + return new Point2D(_x, _y); + } + + @Override + public String toString() + { + return _x + ", " + _y; + } + + @Override + public int hashCode() + { + return Objects.hash(_x, _y); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final Point2D other = (Point2D) obj; + return (_x == other._x) && (_y == other._y); + } + + /** + * @param x : The X coord to test. + * @param y : The Y coord to test. + * @return True if all coordinates equals this {@link Point2D} coordinates. + */ + public boolean equals(int x, int y) + { + return (_x == x) && (_y == y); + } + + public int getX() + { + return _x; + } + + public void setX(int x) + { + _x = x; + } + + public int getY() + { + return _y; + } + + public void setY(int y) + { + _y = y; + } + + public void set(int x, int y) + { + _x = x; + _y = y; + } + + /** + * Refresh the current {@link Point2D} using a reference {@link Point2D} and a distance. The new destination is calculated to go in opposite side of the {@link Point2D} reference.
+ *
+ * This method is perfect to calculate fleeing characters position. + * @param referenceLoc : The Point2D used as position. + * @param distance : The distance to be set between current and new position. + */ + public void setFleeing(Point2D referenceLoc, int distance) + { + final double xDiff = referenceLoc.getX() - _x; + final double yDiff = referenceLoc.getY() - _y; + + final double yxRation = Math.abs(xDiff / yDiff); + + final int y = (int) (distance / (yxRation + 1)); + final int x = (int) (y * yxRation); + + _x += (xDiff < 0 ? x : -x); + _y += (yDiff < 0 ? y : -y); + } + + public void clean() + { + _x = 0; + _y = 0; + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @return The distance between this {@Point2D} and some given coordinates. + */ + public double distance2D(int x, int y) + { + final double dx = (double) _x - x; + final double dy = (double) _y - y; + + return Math.sqrt((dx * dx) + (dy * dy)); + } + + /** + * @param point : The {@link Point2D} to test. + * @return The distance between this {@Point2D} and the {@link Point2D} set as parameter. + */ + public double distance2D(Point2D point) + { + return distance2D(point.getX(), point.getY()); + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of some given coordinates. + */ + public boolean isIn2DRadius(int x, int y, int radius) + { + return distance2D(x, y) < radius; + } + + /** + * @param point : The Point2D to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of the {@link Point2D} set as parameter. + */ + public boolean isIn2DRadius(Point2D point, int radius) + { + return distance2D(point) < radius; + } +} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/AttackableAI.java index 547963e3e8..dc29d4cf6e 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/AttackableAI.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/ai/AttackableAI.java @@ -578,7 +578,7 @@ public class AttackableAI extends CreatureAI } // Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast) - final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); + final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); moveTo(moveLoc.getX(), moveLoc.getY(), moveLoc.getZ()); } } @@ -801,7 +801,7 @@ public class AttackableAI extends CreatureAI final int newZ = npc.getZ() + 30; // Mobius: Verify destination. Prevents wall collision issues and fixes monsters not avoiding obstacles. - moveTo(GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); + moveTo(GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); } return; } diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/data/SpawnTable.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/data/SpawnTable.java index a81201f69e..19f0db5b3e 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/data/SpawnTable.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/data/SpawnTable.java @@ -114,8 +114,8 @@ public class SpawnTable } // XML file for spawn - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); // Write info to XML @@ -192,8 +192,8 @@ public class SpawnTable if (update) { - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = spawn.getNpcSpawnTemplate() != null ? spawn.getNpcSpawnTemplate().getSpawnTemplate().getFile() : new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); final File tempFile = new File(spawnFile.getAbsolutePath().substring(Config.DATAPACK_ROOT.getAbsolutePath().length() + 1).replace('\\', '/') + ".tmp"); try diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/enums/GeoType.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/enums/GeoType.java new file mode 100644 index 0000000000..f77aa5eac4 --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/enums/GeoType.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.enums; + +public enum GeoType +{ + L2J("%d_%d.l2j"), + L2OFF("%d_%d_conv.dat"); + + private final String _filename; + + private GeoType(String filename) + { + _filename = filename; + } + + public String getFilename() + { + return _filename; + } +} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java new file mode 100644 index 0000000000..e62f50a8df --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java @@ -0,0 +1,144 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 9d255a5f54..7bef9c770b 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,72 +16,63 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.channels.FileChannel.MapMode; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.logging.Level; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; import java.util.logging.Logger; import org.l2jmobius.Config; +import org.l2jmobius.commons.util.CommonUtil; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; -import org.l2jmobius.gameserver.geoengine.geodata.IRegion; -import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; -import org.l2jmobius.gameserver.geoengine.geodata.Region; +import org.l2jmobius.gameserver.enums.GeoType; +import org.l2jmobius.gameserver.enums.MoveDirectionType; +import org.l2jmobius.gameserver.geoengine.geodata.ABlock; +import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; +import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; +import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; +import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.model.interfaces.ILocational; -import org.l2jmobius.gameserver.util.GeoUtils; -import org.l2jmobius.gameserver.util.LinePointIterator; -import org.l2jmobius.gameserver.util.LinePointIterator3D; -/** - * @author -Nemesiss-, HorridoJoho - */ public class GeoEngine { - private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - - private static final int WORLD_MIN_X = -655360; - private static final int WORLD_MIN_Y = -589824; - private static final int WORLD_MIN_Z = -16384; - - /** 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; - - /** The regions array */ - private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; - private static final int ELEVATED_SEE_OVER_DISTANCE = 2; - private static final int MAX_SEE_OVER_HEIGHT = 48; - protected GeoEngine() + private final ABlock[][] _blocks; + private final BlockNull _nullBlock; + + // Pre-allocated buffers. + private final BufferHolder[] _buffers; + + public GeoEngine() { LOGGER.info("GeoEngine: Initializing..."); - for (int i = 0; i < _regions.length(); i++) - { - _regions.set(i, NullRegion.INSTANCE); - } + // Initialize block container. + _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; + + // Load null block. + _nullBlock = new BlockNull(); + + // Initialize multilayer temporarily buffer. + BlockMultilayer.initialize(); + + // Load geo files according to geoengine config setup. int loaded = 0; try { @@ -92,23 +83,52 @@ public class GeoEngine final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); if (Files.exists(geoFilePath)) { - try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + // Region file is load-able, try to load it. + if (loadGeoBlocks(regionX, regionY)) { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); loaded++; } } + else + { + // Region file is not load-able, load null blocks. + loadNullBlocks(regionX, regionY); + } } } } catch (Exception e) { - LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + LOGGER.warning("GeoEngine: Failed to load geodata! " + e); System.exit(1); } - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + // Release multilayer block temporarily buffer. + BlockMultilayer.release(); + + String[] array = Config.PATHFIND_BUFFERS.split(";"); + _buffers = new BufferHolder[array.length]; + + int count = 0; + for (int i = 0; i < array.length; i++) + { + String buf = array[i]; + String[] args = buf.split("x"); + + try + { + int size = Integer.parseInt(args[1]); + count += size; + _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); + } + catch (Exception e) + { + LOGGER.warning("Could not load buffer setting:" + buf + ". " + e); + } + } + LOGGER.info("Loaded " + count + " node buffers."); + // Avoid wrong configs when no files are loaded. if ((loaded == 0) && Config.PATHFINDING) { @@ -118,616 +138,913 @@ public class GeoEngine } /** - * @param geoX - * @param geoY - * @return the region + * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer and log this situation. + * @param size : pre-calculated minimal required size + * @return NodeBuffer : buffer */ - private IRegion getRegion(int geoX, int geoY) + private NodeBuffer getBuffer(int size) { - final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); - if ((region < 0) || (region >= _regions.length())) + NodeBuffer current = null; + for (BufferHolder holder : _buffers) { - return null; - } - return _regions.get(region); - } - - /** - * @param filePath - * @param regionX - * @param regionY - * @throws IOException - */ - 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()).load().order(ByteOrder.LITTLE_ENDIAN))); - } - } - - /** - * @param regionX - * @param regionY - */ - public void unloadRegion(int regionX, int regionY) - { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); - } - - /** - * @param geoX - * @param geoY - * @return if geodata exist - */ - public boolean hasGeoPos(int geoX, int geoY) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return false; - } - return region.hasGeo(); - } - - /** - * Checks the specified position for available geodata. - * @param x the world x - * @param y the world y - * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise - */ - public boolean hasGeo(int x, int y) - { - return hasGeoPos(getGeoX(x), getGeoY(y)); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe check - */ - public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return true; - } - return region.checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe anti-corner cut - */ - public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) - { - boolean can = true; - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - return can && checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the nearest Z value - */ - public int getNearestZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNearestZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next lower Z value - */ - public int getNextLowerZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextLowerZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next higher Z value - */ - public int getNextHigherZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextHigherZ(geoX, geoY, worldZ); - } - - /** - * Gets the Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHeight(int x, int y, int z) - { - return getNearestZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next lower Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getLowerHeight(int x, int y, int z) - { - return getNextLowerZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next higher Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHigherHeight(int x, int y, int z) - { - return getNextHigherZ(getGeoX(x), getGeoY(y), z); - } - - /** - * @param worldX - * @return the geo X - */ - public int getGeoX(int worldX) - { - return (worldX - WORLD_MIN_X) / 16; - } - - /** - * @param worldY - * @return the geo Y - */ - public int getGeoY(int worldY) - { - return (worldY - WORLD_MIN_Y) / 16; - } - - /** - * @param worldZ - * @return the geo Z - */ - public int getGeoZ(int worldZ) - { - return (worldZ - WORLD_MIN_Z) / 16; - } - - /** - * @param geoX - * @return the world X - */ - public int getWorldX(int geoX) - { - return (geoX * 16) + WORLD_MIN_X + 8; - } - - /** - * @param geoY - * @return the world Y - */ - public int getWorldY(int geoY) - { - return (geoY * 16) + WORLD_MIN_Y + 8; - } - - /** - * @param geoZ - * @return the world Z - */ - public int getWorldZ(int geoZ) - { - return (geoZ * 16) + WORLD_MIN_Z + 8; - } - - /** - * Can see target. Doors as target always return true. Checks doors between. - * @param cha the character - * @param target the target - * @return {@code true} if the character can see the target (LOS), {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, WorldObject target) - { - if (target.isDoor()) - { - // Can always see doors. - return true; - } - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); - } - - /** - * Can see target. Checks doors between. - * @param cha the character - * @param worldPosition the world position - * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) - { - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param world - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @param tworld the target's instanceId - * @return - */ - public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) - { - return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instance - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) - { - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) - { - return false; - } - return canSeeTarget(x, y, z, tx, ty, tz); - } - - /** - * @param prevX - * @param prevY - * @param prevGeoZ - * @param curX - * @param curY - * @param nswe - * @return the LOS Z value - */ - private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) - { - if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) - { - throw new RuntimeException("Multiple directions!"); - } - - if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) - { - return getNearestZ(curX, curY, prevGeoZ); - } - - return getNextHigherZ(curX, curY, prevGeoZ); - } - - /** - * Can see target. Does not check doors between. - * @param xValue the x coordinate - * @param yValue the y coordinate - * @param zValue the z coordinate - * @param txValue the target's x coordinate - * @param tyValue the target's y coordinate - * @param tzValue the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) - { - int x = xValue; - int y = yValue; - int tx = txValue; - int ty = tyValue; - - int geoX = getGeoX(x); - int geoY = getGeoY(y); - int tGeoX = getGeoX(tx); - int tGeoY = getGeoY(ty); - - int z = getNearestZ(geoX, geoY, zValue); - int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - // Fastpath. - if ((geoX == tGeoX) && (geoY == tGeoY)) - { - if (hasGeoPos(tGeoX, tGeoY)) - { - return z == tz; - } - return true; - } - - if (tz > z) - { - int tmp = tx; - tx = x; - x = tmp; - - tmp = ty; - ty = y; - y = tmp; - - tmp = tz; - tz = z; - z = tmp; - - tmp = tGeoX; - tGeoX = geoX; - geoX = tmp; - - tmp = tGeoY; - tGeoY = geoY; - geoY = tmp; - } - - final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); - // First point is guaranteed to be available, skip it, we can always see our own position. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - final int prevZ = pointIter.z(); - int prevGeoZ = prevZ; - int ptIndex = 0; - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - - if ((curX == prevX) && (curY == prevY)) + // Find proper size of buffer. + if (holder._size < size) { continue; } - final int beeCurZ = pointIter.z(); - int curGeoZ = prevGeoZ; - - // Check if the position has geodata. - if (hasGeoPos(curX, curY)) + // Find unlocked NodeBuffer. + for (NodeBuffer buffer : holder._buffer) { - final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); - curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); - int maxHeight; - if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) + if (!buffer.isLocked()) { - maxHeight = z + MAX_SEE_OVER_HEIGHT; - } - else - { - maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; + continue; } - boolean canSeeThrough = false; - if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) + return buffer; + } + + LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); + + // NodeBuffer not found, allocate temporary buffer. + current = new NodeBuffer(holder._size); + current.isLocked(); + } + + return current; + } + + /** + * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + * @return boolean : True, when geodata file was loaded without problem. + */ + private boolean loadGeoBlocks(int regionX, int regionY) + { + final String filename = String.format(Config.GEODATA_TYPE.getFilename(), regionX, regionY); + final String filepath = Config.GEODATA_PATH + "\\" + filename; + + // Standard load. + try (RandomAccessFile raf = new RandomAccessFile(filepath, "r"); + FileChannel fc = raf.getChannel()) + { + // Initialize file buffer. + MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // Load 18B header for L2OFF geodata (1st and 2nd byte...region X and Y). + if (Config.GEODATA_TYPE == GeoType.L2OFF) + { + for (int i = 0; i < 18; i++) { - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) + buffer.get(); + } + } + + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Loop over region blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + if (Config.GEODATA_TYPE == GeoType.L2J) { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) - { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); + // Get block type. + final byte type = buffer.get(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + case GeoStructure.TYPE_MULTILAYER_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + default: + { + throw new IllegalArgumentException("Unknown block type: " + type); + } + } } else { - canSeeThrough = true; + // Get block type. + final short type = buffer.getShort(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + default: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + } } } + } + + // Check data consistency. + if (buffer.remaining() > 0) + { + LOGGER.warning("Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); + } + + // Loading was successful. + return true; + } + catch (Exception e) + { + // An error occured while loading, load null blocks. + LOGGER.warning("Error loading " + filename + " region file. " + e); + + // Replace whole region file with null blocks. + loadNullBlocks(regionX, regionY); + + // Loading was not successful. + return false; + } + } + + /** + * Loads null blocks. Used when no region file is detected or an error occurs during loading. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + */ + private void loadNullBlocks(int regionX, int regionY) + { + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Load all null blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + _blocks[blockX + ix][blockY + iy] = _nullBlock; + } + } + } + + /** + * Converts world X to geodata X. + * @param worldX + * @return int : Geo X + */ + public static int getGeoX(int worldX) + { + return (worldX - World.WORLD_X_MIN) >> 4; + } + + /** + * Converts world Y to geodata Y. + * @param worldY + * @return int : Geo Y + */ + public static int getGeoY(int worldY) + { + return (worldY - World.WORLD_Y_MIN) >> 4; + } + + /** + * Converts geodata X to world X. + * @param geoX + * @return int : World X + */ + public static int getWorldX(int geoX) + { + return (geoX << 4) + World.WORLD_X_MIN + 8; + } + + /** + * Converts geodata Y to world Y. + * @param geoY + * @return int : World Y + */ + public static int getWorldY(int geoY) + { + return (geoY << 4) + World.WORLD_Y_MIN + 8; + } + + /** + * Returns block of geodata on given coordinates. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return {@link ABlock} : Block of geodata. + */ + public ABlock getBlock(int geoX, int geoY) + { + final int x = geoX / GeoStructure.BLOCK_CELLS_X; + if ((x < 0) || (x >= GeoStructure.GEO_BLOCKS_X)) + { + return null; + } + final int y = geoY / GeoStructure.BLOCK_CELLS_Y; + if ((y < 0) || (y >= GeoStructure.GEO_BLOCKS_Y)) + { + return null; + } + return _blocks[x][y]; + } + + /** + * Check if geo coordinates has geo. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return boolean : True, if given geo coordinates have geodata + */ + public boolean hasGeoPos(int geoX, int geoY) + { + final ABlock block = getBlock(geoX, geoY); + return (block != null) && block.hasGeoPos(); + } + + /** + * Returns the height of cell, which is closest to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell geodata Z coordinate, closest to given coordinates. + */ + public short getHeightNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return (short) worldZ; + } + return block.getHeightNearest(geoX, geoY, worldZ); + } + + /** + * Returns the NSWE flag byte of cell, which is closes to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. + */ + public byte getNsweNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return GeoStructure.CELL_FLAG_ALL; + } + return block.getNsweNearest(geoX, geoY, worldZ); + } + + /** + * Check if world coordinates has geo. + * @param worldX : World X + * @param worldY : World Y + * @return boolean : True, if given world coordinates have geodata + */ + public boolean hasGeo(int worldX, int worldY) + { + return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param loc : The location used as reference. + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(Location loc) + { + return getHeightNearest(getGeoX(loc.getX()), getGeoY(loc.getY()), loc.getZ()); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param worldX : world x + * @param worldY : world y + * @param worldZ : world z + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(int worldX, int worldY, int worldZ) + { + return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); + } + + /** + * Check line of sight from {@link WorldObject} to {@link WorldObject}.
+ * @param object : The origin object. + * @param target : The target object. + * @return True, when object can see target. + */ + public boolean canSeeTarget(WorldObject object, WorldObject target) + { + // Can always see doors. + if (target.isDoor()) + { + return true; + } + + if (object.getInstanceWorld() != target.getInstanceWorld()) + { + return false; + } + + if (DoorData.getInstance().checkIfDoorsBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld())) + { + return false; + } + + // Get object's and target's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + double theight = 0; + if (target instanceof Creature) + { + theight += (((Creature) target).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + return canSee(object.getX(), object.getY(), object.getZ(), oheight, target.getX(), target.getY(), target.getZ(), theight) && canSee(target.getX(), target.getY(), target.getZ(), theight, object.getX(), object.getY(), object.getZ(), oheight); + } + + /** + * Check line of sight from {@link WorldObject} to {@link Location}.
+ * Note: The check uses {@link Location}'s real Z coordinate (e.g. point above ground), not its geodata representation. + * @param object : The origin object. + * @param position : The target position. + * @return True, when object can see position. + */ + public boolean canSeeLocation(WorldObject object, Location position) + { + // Get object and location coordinates. + int ox = object.getX(); + int oy = object.getY(); + int oz = object.getZ(); + int tx = position.getX(); + int ty = position.getY(); + int tz = position.getZ(); + + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld())) + { + return false; + } + + // Get object's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + // Perform geodata check. + return canSee(ox, oy, oz, oheight, tx, ty, tz, 0) && canSee(tx, ty, tz, 0, ox, oy, oz, oheight); + } + + /** + * Simple check for origin to target visibility.
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param oheight : The height of origin, used as start point. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param theight : The height of target, used as end point. + * @return True, when origin can see target. + */ + public boolean canSee(int ox, int oy, int oz, double oheight, int tx, int ty, int tz, double theight) + { + // Get origin geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get target geodata coordinates. + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check being on same cell and layer (index). + // Note: Get index must use origin height increased by cell height, the method returns index to height exclusive self. + int index = block.getIndexBelow(gox, goy, oz + GeoStructure.CELL_HEIGHT); + if (index < 0) + { + return false; + } + + if ((gox == gtx) && (goy == gty)) + { + return index == block.getIndexBelow(gtx, gty, tz + GeoStructure.CELL_HEIGHT); + } + + // Get ground and nswe flag. + int groundZ = block.getHeight(index); + int nswe = block.getNswe(index); + + // Get delta coordinates, slope of line (XY, XZ) and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double dz = (tz + theight) - (oz + oheight); + final double m = (double) dy / dx; + final double mz = dz / Math.sqrt((dx * dx) + (dy * dy)); + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); + + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) + { + // Set next cell in X direction. + gridX += mdt.getStepX(); + gox += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); - if (!canSeeThrough) - { - return false; - } + // Set next cell in Y direction. + gridY += mdt.getStepY(); + goy += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevGeoZ = curGeoZ; - ++ptIndex; + // Get block of the next cell. + block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get line of sight height (including Z slope). + double losz = oz + oheight + Config.MAX_OBSTACLE_HEIGHT; + losz += mz * Math.sqrt(((checkX - ox) * (checkX - ox)) + ((checkY - oy) * (checkY - oy))); + + // Check line of sight going though wall (vertical check). + + // Get index of particular layer, based on last iterated cell conditions. + boolean canMove = (nswe & dir) != 0; + if (canMove) + { + // No wall present, get next cell below current cell. + index = block.getIndexBelow(gox, goy, groundZ + GeoStructure.CELL_IGNORE_HEIGHT); + } + else + { + // Wall present, get next cell above current cell. + index = block.getIndexAbove(gox, goy, groundZ - (2 * GeoStructure.CELL_HEIGHT)); + } + + // Next cell's does not exist (no geodata with valid condition), return fail. + if (index < 0) + { + return false; + } + + // Get next cell's layer height. + int z = block.getHeight(index); + + // Perform sine of sight check (next cell is above line of sight line), return fail. + if (z > losz) + { + return false; + } + + // Next cell is accessible, update z and NSWE. + groundZ = z; + nswe = block.getNswe(index); } + // Iteration is completed, no obstacle is found. return true; } /** - * Move check. - * @param x the x coordinate - * @param y the y coordinate - * @param zValue the z coordinate - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tzValue the target's z coordinate - * @param instance the instance - * @return the last Location (x,y,z) where player can walk - just before wall + * Check movement from coordinates to coordinates.
+ * Note: The Z coordinates are supposed to be already validated geodata coordinates. + * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return True, when target coordinates are reachable from origin coordinates. */ - public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(x); - final int geoY = getGeoY(y); - final int z = getNearestZ(geoX, geoY, zValue); - final int tGeoX = getGeoX(tx); - final int tGeoY = getGeoY(ty); - final int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return new Location(x, y, getHeight(x, y, z)); - } - if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) - { - return new Location(x, y, getHeight(x, y, z)); + return false; } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = z; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return false; + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + int goz = getHeightNearest(gox, goy, oz); + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check movement within same cell. + if ((gox == gtx) && (goy == gty)) + { + return goz == getHeight(tx, ty, tz); + } + + // Get nswe flag. + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - // Can't move, return previous location. - return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check point heading into obstacle, if so return current point. + if ((nswe & dir) == 0) + { + return false; + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return false; + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != tz)) - { - // Different floors, return start location. - return new Location(x, y, z); - } - - return new Location(tx, ty, tz); + // When origin Z is target Z, the move is successful. + return goz == getHeight(tx, ty, tz); } /** - * Checks if its possible to move from one location to another. - * @param fromX the X coordinate to start checking from - * @param fromY the Y coordinate to start checking from - * @param fromZvalue the Z coordinate to start checking from - * @param toX the X coordinate to end checking at - * @param toY the Y coordinate to end checking at - * @param toZvalue the Z coordinate to end checking at - * @param instance the instance - * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + * Check movement from origin to target coordinates. Returns last available point in the checked path.
+ * Target X and Y reachable and Z is on same floor: + *
    + *
  • Location of the target with corrected Z value from geodata.
  • + *
+ * Target X and Y reachable but Z is on another floor: + *
    + *
  • Location of the origin with corrected Z value from geodata.
  • + *
+ * Target X and Y not reachable: + *
    + *
  • Last accessible location in destination to target.
  • + *
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return The {@link Location} representing last point of movement (e.g. just before wall). */ - public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + public Location getValidLocation(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(fromX); - final int geoY = getGeoY(fromY); - final int fromZ = getNearestZ(geoX, geoY, fromZvalue); - final int tGeoX = getGeoX(toX); - final int tGeoY = getGeoY(toY); - final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); - - if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) - { - return false; + return new Location(ox, oy, oz); } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = fromZ; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return new Location(ox, oy, oz); + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + final int gtz = getHeightNearest(gtx, gty, tz); + int goz = getHeightNearest(gox, goy, oz); + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - return false; - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check target cell is out of geodata grid (world coordinates). + if ((nx < 0) || (nx >= GeoStructure.GEO_CELLS_X) || (ny < 0) || (ny >= GeoStructure.GEO_CELLS_Y)) + { + return new Location(checkX, checkY, goz); + } + + // Check point heading into obstacle, if so return current (border) point. + if ((nswe & dir) == 0) + { + return new Location(checkX, checkY, goz); + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current (border) point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return new Location(checkX, checkY, goz); + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) - { - // Different floors. - return false; - } - - return true; + // Compare Z coordinates: + // If same, path is okay, return target point and fix its Z geodata coordinate. + // If not same, path is does not exist, return origin point. + return goz == gtz ? new Location(tx, ty, gtz) : new Location(ox, oy, oz); } + /** + * Returns the list of location objects as a result of complete path calculation. + * @param ox : origin x + * @param oy : origin y + * @param oz : origin z + * @param tx : target x + * @param ty : target y + * @param tz : target z + * @param instance + * @return {@code List} : complete path from nodes + */ + public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + { + // Get origin and check existing geo coords. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + if (!hasGeoPos(gox, goy)) + { + return Collections.emptyList(); + } + + int goz = getHeightNearest(gox, goy, oz); + + // Get target and check existing geo coords. + int gtx = getGeoX(tx); + int gty = getGeoY(ty); + if (!hasGeoPos(gtx, gty)) + { + return Collections.emptyList(); + } + + int gtz = getHeightNearest(gtx, gty, tz); + + // Prepare buffer for pathfinding calculations. + NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + if (buffer == null) + { + return Collections.emptyList(); + } + + // Find path. + List path = null; + try + { + path = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + if (path.isEmpty()) + { + return Collections.emptyList(); + } + } + catch (Exception e) + { + return Collections.emptyList(); + } + finally + { + buffer.free(); + } + + // Check path. + if (path.size() < 3) + { + return path; + } + + // Get path list iterator. + ListIterator point = path.listIterator(); + + // Get node A (origin). + int nodeAx = ox; + int nodeAy = oy; + int nodeAz = goz; + + // Get node B. + Location nodeB = point.next(); + + // Iterate thought the path to optimize it. + while (point.hasNext()) + { + // Get node C. + Location nodeC = path.get(point.nextIndex()); + + // Check movement from node A to node C. + if (canMoveToTarget(nodeAx, nodeAy, nodeAz, nodeC.getX(), nodeC.getY(), nodeC.getZ(), instance)) + { + // Can move from node A to node C. + // Remove node B. + point.remove(); + } + else + { + // Can not move from node A to node C. + // Set node A (node B is part of path, update A coordinates). + nodeAx = nodeB.getX(); + nodeAy = nodeB.getY(); + nodeAz = nodeB.getZ(); + } + + // Set node B. + nodeB = point.next(); + } + + return path; + } + + /** + * NodeBuffer container with specified size and count of separate buffers. + */ + private static class BufferHolder + { + final int _size; + final List _buffer; + + public BufferHolder(int size, int count) + { + _size = size; + _buffer = new ArrayList<>(count); + + for (int i = 0; i < count; i++) + { + _buffer.add(new NodeBuffer(size)); + } + } + } + + /** + * Returns the instance of the {@link GeoEngine}. + * @return {@link GeoEngine} : The instance. + */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -737,4 +1054,4 @@ public class GeoEngine { protected static final GeoEngine INSTANCE = new GeoEngine(); } -} +} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java deleted file mode 100644 index cc76b7523a..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ /dev/null @@ -1,301 +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 . - */ -package org.l2jmobius.gameserver.geoengine; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; -import org.l2jmobius.gameserver.model.World; -import org.l2jmobius.gameserver.model.instancezone.Instance; - -/** - * @author -Nemesiss- - */ -public class GeoEnginePathfinding -{ - private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); - - private BufferInfo[] _buffers; - - protected GeoEnginePathfinding() - { - try - { - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - - _buffers = 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); - } - - _buffers[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"); - } - } - - public boolean pathNodesExist(short regionoffset) - { - return false; - } - - public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) - { - 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)))); - if (buffer == null) - { - return null; - } - - List path = null; - try - { - final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); - - if (result == null) - { - return null; - } - - path = constructPath(result); - } - catch (Exception e) - { - LOGGER.warning(e.getMessage()); - return null; - } - finally - { - buffer.free(); - } - - // check path - if (path.size() < 3) - { - return path; - } - - int currentX, currentY, currentZ; - ListIterator middlePoint; - - middlePoint = path.listIterator(); - currentX = x; - currentY = y; - currentZ = z; - - while (middlePoint.hasNext()) - { - final AbstractNodeLoc locMiddle = middlePoint.next(); - if (!middlePoint.hasNext()) - { - break; - } - - final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); - if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) - { - middlePoint.remove(); - } - else - { - currentX = locMiddle.getX(); - currentY = locMiddle.getY(); - currentZ = locMiddle.getZ(); - } - } - - return path; - } - - /** - * Convert geodata position to pathnode position - * @param geo_pos - * @return pathnode position - */ - public short getNodePos(int geo_pos) - { - return (short) (geo_pos >> 3); // OK? - } - - /** - * Convert node position to pathnode block position - * @param node_pos - * @return pathnode block position (0...255) - */ - public short getNodeBlock(int node_pos) - { - return (short) (node_pos % 256); - } - - public byte getRegionX(int node_pos) - { - return (byte) ((node_pos >> 8) + World.TILE_X_MIN); - } - - public byte getRegionY(int node_pos) - { - return (byte) ((node_pos >> 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 node_x rx - * @return - */ - public int calculateWorldX(short node_x) - { - return World.MAP_MIN_X + (node_x * 128) + 48; - } - - /** - * Convert pathnode y to World y position - * @param node_y - * @return - */ - public int calculateWorldY(short node_y) - { - return World.MAP_MIN_Y + (node_y * 128) + 48; - } - - private List constructPath(AbstractNode nodeValue) - { - final LinkedList path = new LinkedList<>(); - int previousDirectionX = Integer.MIN_VALUE; - int previousDirectionY = Integer.MIN_VALUE; - int directionX, directionY; - - AbstractNode node = nodeValue; - while (node.getParent() != null) - { - directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); - directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); - - // only add a new route point if moving direction changes - if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) - { - previousDirectionX = directionX; - previousDirectionY = directionY; - - path.addFirst(node.getLoc()); - node.setLoc(null); - } - - node = node.getParent(); - } - - return path; - } - - private CellNodeBuffer alloc(int size) - { - CellNodeBuffer current = null; - for (BufferInfo i : _buffers) - { - if (i.mapSize >= size) - { - for (CellNodeBuffer buf : i.buffer) - { - if (buf.lock()) - { - current = buf; - break; - } - } - if (current != null) - { - break; - } - - // not found, allocate temporary buffer - current = new CellNodeBuffer(i.mapSize); - current.lock(); - if (i.buffer.size() < i.count) - { - i.buffer.add(current); - break; - } - } - } - - return current; - } - - private static final class BufferInfo - { - final int mapSize; - final int count; - ArrayList buffer; - - public BufferInfo(int size, int cnt) - { - mapSize = size; - count = cnt; - buffer = new ArrayList<>(count); - } - } - - public static GeoEnginePathfinding getInstance() - { - return SingletonHolder.INSTANCE; - } - - private static class SingletonHolder - { - protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); - } -} diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java new file mode 100644 index 0000000000..49ec35aa1c --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java @@ -0,0 +1,85 @@ +/* + * 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 . + */ +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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @param index : Index of the cell. + * @return short : Cell geodata Z coordinate, below given coordinates. + */ + public abstract byte getNswe(int index); +} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java new file mode 100644 index 0000000000..f5997bf287 --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java @@ -0,0 +1,130 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java new file mode 100644 index 0000000000..9af9d2a28a --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java @@ -0,0 +1,95 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java new file mode 100644 index 0000000000..31fbf5d477 --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java new file mode 100644 index 0000000000..f77d21d84b --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java @@ -0,0 +1,68 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +public class BlockNull extends ABlock +{ + @Override + public boolean hasGeoPos() + { + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java deleted file mode 100644 index 61ea4fcf82..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java +++ /dev/null @@ -1,48 +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 . - */ -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() - { - } -} diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java deleted file mode 100644 index 3c8de1a7f7..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java +++ /dev/null @@ -1,77 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java deleted file mode 100644 index 1ccdefe3da..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java +++ /dev/null @@ -1,56 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java new file mode 100644 index 0000000000..691b164e0c --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ +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_ALL = 0x0F; + + // 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; +} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java deleted file mode 100644 index 25eafc5a04..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java +++ /dev/null @@ -1,42 +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 . - */ -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); -} diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java deleted file mode 100644 index 0d2c765002..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java +++ /dev/null @@ -1,47 +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 . - */ -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(); -} diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java deleted file mode 100644 index 52df457360..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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); - - // 1 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; - } -} diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java deleted file mode 100644 index 72a5a635c7..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java +++ /dev/null @@ -1,55 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author HorridoJoho - */ -public final class NullRegion implements IRegion -{ - public static final NullRegion INSTANCE = new NullRegion(); - - @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 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; - } -} diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java deleted file mode 100644 index fc924cb875..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java +++ /dev/null @@ -1,92 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java deleted file mode 100644 index 5087513ed9..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java +++ /dev/null @@ -1,87 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public abstract class AbstractNode -{ - private T _loc; - private AbstractNode _parent; - - public AbstractNode(T loc) - { - _loc = loc; - } - - public void setParent(AbstractNode p) - { - _parent = p; - } - - public AbstractNode getParent() - { - return _parent; - } - - public T getLoc() - { - return _loc; - } - - public void setLoc(T l) - { - _loc = l; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); - return result; - } - - @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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java deleted file mode 100644 index 7223b5b2be..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java +++ /dev/null @@ -1,33 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -/** - * @author -Nemesiss- - */ -public abstract class AbstractNodeLoc -{ - public abstract int getX(); - - public abstract int getY(); - - public abstract int getZ(); - - public abstract int getNodeX(); - - public abstract int getNodeY(); -} diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java deleted file mode 100644 index 3425234e0e..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public class CellNode extends AbstractNode -{ - 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java deleted file mode 100644 index 522610bb7c..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java +++ /dev/null @@ -1,348 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -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 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) - { - _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(); - } - - public final List debugPath() - { - final List 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 void getNeighbors() - { - if (!_current.getLoc().canGoAll()) - { - 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); - } - - // SouthEast - if ((nodeE != null) && (nodeS != null)) - { - if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) - { - addNode(x + 1, y + 1, z, true); - } - } - - // SouthWest - if ((nodeS != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) - { - addNode(x - 1, y + 1, z, true); - } - } - - // NorthEast - if ((nodeN != null) && (nodeE != null)) - { - if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) - { - addNode(x + 1, y - 1, z, true); - } - } - - // NorthWest - if ((nodeN != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) - { - addNode(x - 1, y - 1, z, true); - } - } - } - - private 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(); - // reinit node if needed - if (result.getLoc() != null) - { - result.getLoc().set(x, y, z); - } - else - { - result.setLoc(new NodeLoc(x, y, z)); - } - } - - return result; - } - - private 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 boolean isHighWeight(int x, int y, int z) - { - final CellNode result = getNode(x, y, z); - if (result == null) - { - return true; - } - - if (!result.getLoc().canGoAll()) - { - return true; - } - if (Math.abs(result.getLoc().getZ() - z) > 16) - { - return true; - } - - return false; - } - - private 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java new file mode 100644 index 0000000000..fa5ca43c2f --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -0,0 +1,111 @@ +/* + * 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 . + */ +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 geodata values. + private int _geoX; + private int _geoY; + private byte _nswe; + + // 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; + + _costG = 0; + _costH = 0; + _costF = 0; + + _parent = null; + } + + public void setGeo(int gx, int gy, int gz, byte nswe) + { + super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); + + _geoX = gx; + _geoY = gy; + _nswe = nswe; + } + + 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 int getCostF() + { + return _costF; + } + + public Node getParent() + { + return _parent; + } + + @Override + public int compareTo(Node o) + { + return _costF - o._costF; + } +} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java new file mode 100644 index 0000000000..b464212c96 --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -0,0 +1,368 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.awt.Color; +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; +import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; + +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 _opened; + // Container holding Nodes already explored. + private final List _closed; + + // Target coordinates. + private int _gtx; + private int _gty; + private int _gtz; + + // Pathfinding statistics. + private long _timeStamp; + private long _lastElapsedTime; + + 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 findPath(int gox, int goy, int goz, int gtx, int gty, int gtz) + { + // Set start timestamp. + _timeStamp = System.currentTimeMillis(); + + // 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)); + _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 constructPath() + { + // Create result. + final LinkedList 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; + } + + /** + * Creates list of Nodes to show debug path. + * @param debug : The debug packet to add debug informations in. + */ + public void debugPath(ExServerPrimitive debug) + { + // Add all opened node as yellow points. + for (Node n : _opened) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); + } + + // Add all opened node as blue points. + for (Node n : _closed) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); + } + } + + 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; + + _lastElapsedTime = System.currentTimeMillis() - _timeStamp; + _lock.unlock(); + } + + public long getElapsedTime() + { + return _lastElapsedTime; + } + + /** + * Expand the current {@link Node} by exploring its neighbors (axially and diagonally). + */ + private void expand() + { + // Movement is blocked, skip. + final byte nswe = _current.getNSWE(); + if (nswe == 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; + + // Can move north, expand. + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + } + + // Can move south, expand. + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + } + + // Can move west, expand. + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move east, expand. + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move north-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move north-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + } + + /** + * 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 weight : The weight of movement to the new node. + * @return The nswe of the added node. Blank, if not added. + */ + private byte addNode(int gx, int gy, int gzValue, int weight) + { + // 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]; + + // Set node geodata coordinates. + node.setGeo(gx, gy, gz, nswe); + + // 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, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + _opened.add(node); + _bufferIndex++; + return nswe; + } + + /** + * Calculate cost H value, calculated using diagonal distance method.
+ * 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. + final int dx = Math.abs(gx - _gtx); + final int dy = Math.abs(gy - _gty); + final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + + // Get diagonal and axial differences to the target. + final int dd = Math.min(dx, dy); + final int da = Math.max(dx, dy) - dd; + + // Calculate the diagonal distance of the node to the target. + return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + } +} \ No newline at end of file diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java deleted file mode 100644 index e3bad04b25..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; - -/** - * @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 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 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; - // return super.hashCode(); - } - - @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; - if (_x != other._x) - { - return false; - } - if (_y != other._y) - { - return false; - } - if (_goNorth != other._goNorth) - { - return false; - } - if (_goEast != other._goEast) - { - return false; - } - if (_goSouth != other._goSouth) - { - return false; - } - if (_goWest != other._goWest) - { - return false; - } - if (_geoHeight != other._geoHeight) - { - return false; - } - return true; - } -} diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java index 1faf1ae423..ed261765e1 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java @@ -93,15 +93,15 @@ public class ZoneManager implements IXmlReader private static final Map SETTINGS = new HashMap<>(); private static final int SHIFT_BY = 15; - private static final int OFFSET_X = Math.abs(World.MAP_MIN_X >> SHIFT_BY); - private static final int OFFSET_Y = Math.abs(World.MAP_MIN_Y >> SHIFT_BY); + private static final int OFFSET_X = Math.abs(World.WORLD_X_MIN >> SHIFT_BY); + private static final int OFFSET_Y = Math.abs(World.WORLD_Y_MIN >> SHIFT_BY); private final Map, ConcurrentHashMap> _classZones = new ConcurrentHashMap<>(); private final Map _spawnTerritories = new ConcurrentHashMap<>(); private final AtomicInteger _lastDynamicId = new AtomicInteger(300000); private List _debugItems; - private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.MAP_MAX_X >> SHIFT_BY) + OFFSET_X + 1][(World.MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y + 1]; + private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.WORLD_X_MAX >> SHIFT_BY) + OFFSET_X + 1][(World.WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y + 1]; /** * Instantiates a new zone manager. diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/Location.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/Location.java index 73689ab1a6..ca3b22a3f0 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/Location.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/Location.java @@ -16,30 +16,30 @@ */ package org.l2jmobius.gameserver.model; +import java.util.Objects; + +import org.l2jmobius.commons.util.Point2D; import org.l2jmobius.gameserver.model.interfaces.ILocational; import org.l2jmobius.gameserver.model.interfaces.IPositionable; /** - * Location data transfer object.
- * Contains coordinates data, heading and instance Id. - * @author Zoey76 + * A datatype used to retain a 3D (x/y/z/heading) point. It got the capability to be set and cleaned. */ -public class Location implements IPositionable +public class Location extends Point2D implements IPositionable { - protected int _x; - protected int _y; - protected int _z; - private int _heading; + protected volatile int _z; + protected volatile int _heading; public Location(int x, int y, int z) { - this(x, y, z, 0); + super(x, y); + _z = z; + _heading = 0; } public Location(int x, int y, int z, int heading) { - _x = x; - _y = y; + super(x, y); _z = z; _heading = heading; } @@ -51,9 +51,8 @@ public class Location implements IPositionable public Location(StatSet set) { - _x = set.getInt("x"); - _y = set.getInt("y"); - _z = set.getInt("z"); + super(set.getInt("x", 0), set.getInt("y", 0)); + _z = set.getInt("z", 0); _heading = set.getInt("heading", 0); } @@ -146,6 +145,25 @@ public class Location implements IPositionable _heading = loc.getHeading(); } + @Override + public void clean() + { + super.clean(); + _z = 0; + } + + @Override + public Location clone() + { + return new Location(_x, _y, _z); + } + + @Override + public int hashCode() + { + return (31 * super.hashCode()) + Objects.hash(_z); + } + @Override public boolean equals(Object obj) { diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/World.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/World.java index c1510e8c0e..0d796dd834 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/World.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/World.java @@ -66,19 +66,19 @@ public class World public static final int TILE_Y_MAX = 26; public static final int TILE_ZERO_COORD_X = 20; public static final int TILE_ZERO_COORD_Y = 18; - public static final int MAP_MIN_X = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; - public static final int MAP_MIN_Y = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; + public static final int WORLD_X_MIN = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; + public static final int WORLD_Y_MIN = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; - public static final int MAP_MAX_X = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; - public static final int MAP_MAX_Y = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; + public static final int WORLD_X_MAX = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; + public static final int WORLD_Y_MAX = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; /** Calculated offset used so top left region is 0,0 */ - public static final int OFFSET_X = Math.abs(MAP_MIN_X >> SHIFT_BY); - public static final int OFFSET_Y = Math.abs(MAP_MIN_Y >> SHIFT_BY); + public static final int OFFSET_X = Math.abs(WORLD_X_MIN >> SHIFT_BY); + public static final int OFFSET_Y = Math.abs(WORLD_Y_MIN >> SHIFT_BY); /** Number of regions. */ - private static final int REGIONS_X = (MAP_MAX_X >> SHIFT_BY) + OFFSET_X; - private static final int REGIONS_Y = (MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y; + private static final int REGIONS_X = (WORLD_X_MAX >> SHIFT_BY) + OFFSET_X; + private static final int REGIONS_Y = (WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y; /** Map containing all the players in game. */ private static final Map _allPlayers = new ConcurrentHashMap<>(); @@ -817,9 +817,6 @@ public class World return _memberInPartyNumber.get(); } - /** - * @return the current instance of World - */ public static World getInstance() { return SingletonHolder.INSTANCE; diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/WorldObject.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/WorldObject.java index 0e5536bbc0..62313ffea1 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/WorldObject.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/WorldObject.java @@ -188,23 +188,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif synchronized (this) { int spawnX = x; - if (spawnX > World.MAP_MAX_X) + if (spawnX > World.WORLD_X_MAX) { - spawnX = World.MAP_MAX_X - 5000; + spawnX = World.WORLD_X_MAX - 5000; } - if (spawnX < World.MAP_MIN_X) + if (spawnX < World.WORLD_X_MIN) { - spawnX = World.MAP_MIN_X + 5000; + spawnX = World.WORLD_X_MIN + 5000; } int spawnY = y; - if (spawnY > World.MAP_MAX_Y) + if (spawnY > World.WORLD_Y_MAX) { - spawnY = World.MAP_MAX_Y - 5000; + spawnY = World.WORLD_Y_MAX - 5000; } - if (spawnY < World.MAP_MIN_Y) + if (spawnY < World.WORLD_Y_MIN) { - spawnY = World.MAP_MIN_Y + 5000; + spawnY = World.WORLD_Y_MIN + 5000; } // Set the x,y,z position of the WorldObject. If flagged with _isSpawned, setXYZ will automatically update world region, so avoid that. @@ -518,23 +518,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif public void setXYZInvisible(int x, int y, int z) { int correctX = x; - if (correctX > World.MAP_MAX_X) + if (correctX > World.WORLD_X_MAX) { - correctX = World.MAP_MAX_X - 5000; + correctX = World.WORLD_X_MAX - 5000; } - if (correctX < World.MAP_MIN_X) + if (correctX < World.WORLD_X_MIN) { - correctX = World.MAP_MIN_X + 5000; + correctX = World.WORLD_X_MIN + 5000; } int correctY = y; - if (correctY > World.MAP_MAX_Y) + if (correctY > World.WORLD_Y_MAX) { - correctY = World.MAP_MAX_Y - 5000; + correctY = World.WORLD_Y_MAX - 5000; } - if (correctY < World.MAP_MIN_Y) + if (correctY < World.WORLD_Y_MIN) { - correctY = World.MAP_MIN_Y + 5000; + correctY = World.WORLD_Y_MIN + 5000; } setXYZ(correctX, correctY, z); diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/actor/Creature.java index 47987924ee..127e8c7644 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -65,8 +65,6 @@ 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.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -2585,7 +2583,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3406,8 +3404,8 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe final int originalX = x; final int originalY = y; final int originalZ = z; - final int gtx = (originalX - World.MAP_MIN_X) >> 4; - final int gty = (originalY - World.MAP_MIN_Y) >> 4; + final int gtx = (originalX - World.WORLD_X_MIN) >> 4; + final int gty = (originalY - World.WORLD_Y_MIN) >> 4; if (isOnGeodataPath()) { try @@ -3430,7 +3428,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe && !(((curZ - z) > 300) && (distance < 300))) // Prohibit correcting destination if character wants to fall. { // location different if destination wasn't reached (or just z coord is different) - final Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(curX, curY, curZ, x, y, z, getInstanceWorld()); + final Location destiny = GeoEngine.getInstance().getValidLocation(curX, curY, curZ, x, y, z, getInstanceWorld()); x = destiny.getX(); y = destiny.getY(); z = destiny.getZ(); @@ -3444,7 +3442,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { if (isPlayer() && !_isFlying && !isInWater) diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/actor/Summon.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/actor/Summon.java index 6065b24f61..919cda941e 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/actor/Summon.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/actor/Summon.java @@ -106,7 +106,7 @@ public abstract class Summon extends Playable final int x = owner.getX(); final int y = owner.getY(); final int z = owner.getZ(); - final Location location = GeoEngine.getInstance().canMoveToTargetLoc(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, owner.getInstanceWorld()); + final Location location = GeoEngine.getInstance().getValidLocation(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, getInstanceWorld()); setXYZInvisible(location.getX(), location.getY(), location.getZ()); } diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java index abbe632775..8943842d96 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java @@ -1569,7 +1569,7 @@ public class ItemInstance extends WorldObject if (dropper != null) { final Instance instance = dropper.getInstanceWorld(); - final Location dropDest = GeoEngine.getInstance().canMoveToTargetLoc(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); + final Location dropDest = GeoEngine.getInstance().getValidLocation(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); x = dropDest.getX(); y = dropDest.getY(); z = dropDest.getZ(); diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java index 4659b0a0cd..a7066c227f 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java @@ -1179,7 +1179,7 @@ public class SkillCaster implements Runnable } } - final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().canMoveToTargetLoc(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); + final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().getValidLocation(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); creature.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); creature.broadcastPacket(new FlyToLocation(creature, destination, flyType, 0, 0, 333)); diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java index c62c821f44..30068134e3 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java @@ -114,7 +114,7 @@ public class ValidatePosition implements IClientIncomingPacket { if (player.isFalling(_z)) { - final int nearestZ = GeoEngine.getInstance().getHigherHeight(_x, _y, _z); + final int nearestZ = GeoEngine.getInstance().getHeight(_x, _y, _z); if (player.getZ() < nearestZ) { player.setXYZ(_x, _y, nearestZ); diff --git a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/util/GeoUtils.java index 4600a0fc53..19a979819e 100644 --- a/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_5.0_Salvation/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -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.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; @@ -30,21 +30,21 @@ public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getInstance().getWorldX(iter.x()); - final int wy = GeoEngine.getInstance().getWorldY(iter.y()); + final int wx = GeoEngine.getWorldX(iter.x()); + final int wy = GeoEngine.getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public final class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); - int wy = GeoEngine.getInstance().getWorldY(prevY); + int wx = GeoEngine.getWorldX(prevX); + int wy = GeoEngine.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.getInstance().getWorldX(curX); - wy = GeoEngine.getInstance().getWorldY(curY); + wx = GeoEngine.getWorldX(curX); + wy = GeoEngine.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().checkNearestNswe(x, y, z, nswe)) + if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0) { return Color.GREEN; } @@ -109,9 +109,8 @@ public final class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final GeoEngine ge = GeoEngine.getInstance(); - final int playerGx = ge.getGeoX(player.getX()); - final int playerGy = ge.getGeoY(player.getY()); + final int playerGx = GeoEngine.getGeoX(player.getX()); + final int playerGy = GeoEngine.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -135,32 +134,32 @@ public final class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = ge.getWorldX(gx); - final int y = ge.getWorldY(gy); - final int z = ge.getNearestZ(gx, gy, player.getZ()); + final int x = GeoEngine.getWorldX(gx); + final int y = GeoEngine.getWorldY(gy); + final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); + Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); 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, Cell.NSWE_EAST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); 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, Cell.NSWE_SOUTH); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); 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, Cell.NSWE_WEST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); 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); @@ -188,41 +187,41 @@ public final class GeoUtils { if (y > lastY) { - return Cell.NSWE_SOUTH_EAST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E; } else if (y < lastY) { - return Cell.NSWE_NORTH_EAST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E; } else { - return Cell.NSWE_EAST; + return GeoStructure.CELL_FLAG_E; } } else if (x < lastX) // west { if (y > lastY) { - return Cell.NSWE_SOUTH_WEST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W; } else if (y < lastY) { - return Cell.NSWE_NORTH_WEST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W; } else { - return Cell.NSWE_WEST; + return GeoStructure.CELL_FLAG_W; } } else // unchanged x { if (y > lastY) { - return Cell.NSWE_SOUTH; + return GeoStructure.CELL_FLAG_S; } else if (y < lastY) { - return Cell.NSWE_NORTH; + return GeoStructure.CELL_FLAG_N; } else { diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/config/GeoEngine.ini b/L2J_Mobius_5.5_EtinasFate/dist/game/config/GeoEngine.ini index 609e8a89a7..e740c678dd 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/config/GeoEngine.ini @@ -6,33 +6,44 @@ # at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ 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 + # ================================================================= -# Path finding +# Pathfinding # ================================================================= # 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 +# an alternative path (e.g. walk around obstacle), default: True +PathFinding = True -# Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 -PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 +# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 -# Weight for nodes without obstacles far from walls -LowWeight = 0.5 +# Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 +MoveWeight = 10 +MoveWeightDiag = 14 -# Weight for nodes near walls -MediumWeight = 2 +# 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 -# Weight for nodes with obstacles -HighWeight = 3 +# 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. -# Default: LowWeight * sqrt(2) -DiagonalWeight = 0.707 +# Maximum number of generated nodes per one path-finding process, default 3500 +MaxIterations = 3500 # ================================================================= -# Other +# Line of Sight # ================================================================= -# Correct player Z after falling through the ground. -CorrectPlayerZ = False +# 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 diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/geodata/L2D_GeoEngine.patch b/L2J_Mobius_5.5_EtinasFate/dist/game/data/geodata/L2D_GeoEngine.patch deleted file mode 100644 index f8577e3a0e..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/geodata/L2D_GeoEngine.patch +++ /dev/null @@ -1,6035 +0,0 @@ -Index: dist/game/GeoDataConverter.bat -=================================================================== ---- dist/game/GeoDataConverter.bat (nonexistent) -+++ dist/game/GeoDataConverter.bat (working copy) -@@ -0,0 +1,6 @@ -+@echo off -+title L2D geodata converter -+ -+java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter -+ -+pause -Index: dist/game/GeoDataConverter.sh -=================================================================== ---- dist/game/GeoDataConverter.sh (nonexistent) -+++ dist/game/GeoDataConverter.sh (working copy) -@@ -0,0 +1,4 @@ -+#! /bin/sh -+ -+java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 -+ -Index: dist/game/config/GeoEngine.ini -=================================================================== ---- dist/game/config/GeoEngine.ini (revision 8409) -+++ dist/game/config/GeoEngine.ini (working copy) -@@ -1,6 +1,10 @@ - # ================================================================= - # Geodata - # ================================================================= -+# Because of real-time performance we are using geodata files only in -+# diagonal L2D format now (using filename e.g. 22_16.l2d). -+# L2D geodata can be obtained by conversion of existing L2J or L2OFF geodata. -+# Launch "GeoDataConverter.bat/sh" and follow instructions to start the conversion. - - # 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/ -@@ -13,6 +17,16 @@ - CoordSynchronize = 2 - - # ================================================================= -+# Path checking -+# ================================================================= -+ -+# 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 finding - # ================================================================= - -@@ -23,19 +37,23 @@ - # Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - --# Weight for nodes without obstacles far from walls --LowWeight = 0.5 -+# Base path weight, when moving from one node to another on axis direction, default: 10 -+BaseWeight = 10 - --# Weight for nodes near walls --MediumWeight = 2 -+# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -+DiagonalWeight = 14 - --# Weight for nodes with obstacles --HighWeight = 3 -+# When movement flags of target node is blocked to any direction, multiply movement weight by this multiplier. -+# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 10 -+ObstacleMultiplier = 10 - --# Weight for diagonal movement. --# Default: LowWeight * sqrt(2) --DiagonalWeight = 0.707 -+# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 20 -+# For proper function must be higher than BaseWeight and/or DiagonalWeight. -+HeuristicWeight = 20 - -+# Maximum number of generated nodes per one path-finding process, default 3500 -+MaxIterations = 3500 -+ - # ================================================================= - # Other - # ================================================================= -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (working copy) -@@ -55,9 +55,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -@@ -73,9 +72,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (working copy) -@@ -18,11 +18,11 @@ - - import java.util.List; - --import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -+import org.l2jmobius.gameserver.geoengine.GeoEngine; - import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -+import org.l2jmobius.gameserver.network.SystemMessageId; - import org.l2jmobius.gameserver.util.BuilderUtil; - - public class AdminPathNode implements IAdminCommandHandler -@@ -37,29 +37,30 @@ - { - if (command.equals("admin_path_find")) - { -- if (!Config.PATHFINDING) -- { -- BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); -- return true; -- } - if (activeChar.getTarget() != null) - { -- final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); -+ final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); - if (path == null) - { -- BuilderUtil.sendSysMessage(activeChar, "No Route!"); -- return true; -+ BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); - } -- for (AbstractNodeLoc a : path) -+ else - { -- BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); -+ for (Location point : path) -+ { -+ BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); -+ } - } - } - else - { -- BuilderUtil.sendSysMessage(activeChar, "No Target!"); -+ activeChar.sendPacket(SystemMessageId.INVALID_TARGET); - } - } -+ else -+ { -+ return false; -+ } - return true; - } - -Index: java/org/l2jmobius/Config.java -=================================================================== ---- java/org/l2jmobius/Config.java (revision 8409) -+++ java/org/l2jmobius/Config.java (working copy) -@@ -32,7 +32,6 @@ - import java.net.UnknownHostException; - import java.nio.charset.StandardCharsets; - import java.nio.file.Files; --import java.nio.file.Path; - import java.nio.file.Paths; - import java.time.Duration; - import java.util.ArrayList; -@@ -966,14 +965,17 @@ - // -------------------------------------------------- - // GeoEngine - // -------------------------------------------------- -- public static Path GEODATA_PATH; -+ public static String GEODATA_PATH; -+ public static int COORD_SYNCHRONIZE; -+ public static int PART_OF_CHARACTER_HEIGHT; -+ public static int MAX_OBSTACLE_HEIGHT; - public static boolean PATHFINDING; - public static String PATHFIND_BUFFERS; -- public static float LOW_WEIGHT; -- public static float MEDIUM_WEIGHT; -- public static float HIGH_WEIGHT; -- public static float DIAGONAL_WEIGHT; -- public static int COORD_SYNCHRONIZE; -+ public static int BASE_WEIGHT; -+ public static int DIAGONAL_WEIGHT; -+ public static int HEURISTIC_WEIGHT; -+ public static int OBSTACLE_MULTIPLIER; -+ public static int MAX_ITERATIONS; - public static boolean CORRECT_PLAYER_Z; - - /** Attribute System */ -@@ -2568,14 +2570,17 @@ - - // Load GeoEngine config file (if exists) - final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); -- GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); -+ GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); -+ COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); -+ MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); - PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -- LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); -- MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); -- HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); -- DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); -- COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); -+ DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); -+ OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); -+ HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); -+ MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); - CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); - - // Load AllowedPlayerRaces config file (if exists) -Index: java/org/l2jmobius/gameserver/geoengine/GeoEngine.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (revision 8435) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (working copy) -@@ -16,100 +16,90 @@ - */ - package org.l2jmobius.gameserver.geoengine; - --import java.io.IOException; -+import java.io.File; - import java.io.RandomAccessFile; - import java.nio.ByteOrder; --import java.nio.channels.FileChannel.MapMode; --import java.nio.file.Files; --import java.nio.file.Path; --import java.util.concurrent.atomic.AtomicReferenceArray; --import java.util.logging.Level; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.List; - import java.util.logging.Logger; - - import org.l2jmobius.Config; - import org.l2jmobius.gameserver.data.xml.DoorData; - import org.l2jmobius.gameserver.data.xml.FenceData; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; --import org.l2jmobius.gameserver.geoengine.geodata.IRegion; --import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; --import org.l2jmobius.gameserver.geoengine.geodata.Region; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.World; - import org.l2jmobius.gameserver.model.WorldObject; -+import org.l2jmobius.gameserver.model.actor.Creature; - import org.l2jmobius.gameserver.model.instancezone.Instance; --import org.l2jmobius.gameserver.model.interfaces.ILocational; --import org.l2jmobius.gameserver.util.GeoUtils; --import org.l2jmobius.gameserver.util.LinePointIterator; --import org.l2jmobius.gameserver.util.LinePointIterator3D; -+import org.l2jmobius.gameserver.util.MathUtil; - - /** -- * @author -Nemesiss-, HorridoJoho -+ * @author Hasha - */ - public class GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); -+ protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - -- private static final int WORLD_MIN_X = -655360; -- private static final int WORLD_MIN_Y = -589824; -- private static final int WORLD_MIN_Z = -16384; -+ private final ABlock[][] _blocks; -+ private final BlockNull _nullBlock; - -- /** 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; -- -- /** The regions array */ -- private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); -- -- private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; -- private static final int ELEVATED_SEE_OVER_DISTANCE = 2; -- private static final int MAX_SEE_OVER_HEIGHT = 48; -- -- protected GeoEngine() -+ /** -+ * GeoEngine constructor. Loads all geodata files of chosen geodata format. -+ */ -+ public GeoEngine() - { - LOGGER.info("GeoEngine: Initializing..."); -- for (int i = 0; i < _regions.length(); i++) -- { -- _regions.set(i, NullRegion.INSTANCE); -- } - -+ // initialize block container -+ _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; -+ -+ // load null block -+ _nullBlock = new BlockNull(); -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files according to geoengine config setup - int loaded = 0; -- try -+ long fileSize = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { -- for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { -- for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) -+ final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); -+ if (f.exists() && !f.isDirectory()) - { -- final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); -- if (Files.exists(geoFilePath)) -+ // region file is load-able, try to load it -+ if (loadGeoBlocks(rx, ry)) - { -- try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) -- { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -- loaded++; -- } -+ loaded++; -+ fileSize += f.length(); - } - } -+ else -+ { -+ // region file is not load-able, load null blocks -+ loadNullBlocks(rx, ry); -+ } - } - } -- catch (Exception e) -+ -+ LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -+ if (loaded > 0) - { -- LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); -- System.exit(1); -+ LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); - } - -- LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -- -- // Avoid wrong configs when no files are loaded. -+ // avoid wrong configs when no files are loaded - if (loaded == 0) - { - if (Config.PATHFINDING) -@@ -123,619 +113,820 @@ - LOGGER.info("GeoEngine: Forcing CoordSynchronize setting to -1."); - } - } -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); - } - - /** -- * @param geoX -- * @param geoY -- * @return the region -+ * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. -+ * @return boolean : True, when geodata file was loaded without problem. - */ -- private IRegion getRegion(int geoX, int geoY) -+ private final boolean loadGeoBlocks(int regionX, int regionY) - { -- final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); -- if ((region < 0) || (region >= _regions.length())) -+ final String filename = String.format(GeoFormat.L2D.getFilename(), regionX, regionY); -+ -+ // standard load -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) - { -- return null; -+ // initialize file buffer -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, GeoFormat.L2D); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ } -+ -+ // check data consistency -+ if (buffer.remaining() > 0) -+ { -+ LOGGER.warning("GeoEngine: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ } -+ -+ // loading was successful -+ return true; - } -- return _regions.get(region); -- } -- -- /** -- * @param filePath -- * @param regionX -- * @param regionY -- * @throws IOException -- */ -- 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")) -+ catch (Exception e) - { -- _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -+ // an error occured while loading, load null blocks -+ LOGGER.warning("GeoEngine: Error while loading " + filename + " region file."); -+ LOGGER.warning(e.getMessage()); -+ -+ // replace whole region file with null blocks -+ loadNullBlocks(regionX, regionY); -+ -+ // loading was not successful -+ return false; - } - } - - /** -- * @param regionX -- * @param regionY -+ * Loads null blocks. Used when no region file is detected or an error occurs during loading. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. - */ -- public void unloadRegion(int regionX, int regionY) -+ private final void loadNullBlocks(int regionX, int regionY) - { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); -- } -- -- /** -- * @param geoX -- * @param geoY -- * @return if geodata exist -- */ -- public boolean hasGeoPos(int geoX, int geoY) -- { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // load all null blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { -- return false; -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[blockX + ix][blockY + iy] = _nullBlock; -+ } - } -- return region.hasGeo(); - } - -+ // GEODATA - GENERAL -+ - /** -- * Checks the specified position for available geodata. -- * @param x the world x -- * @param y the world y -- * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise -+ * Converts world X to geodata X. -+ * @param worldX -+ * @return int : Geo X - */ -- public boolean hasGeo(int x, int y) -+ public static int getGeoX(int worldX) - { -- return hasGeoPos(getGeoX(x), getGeoY(y)); -+ return (MathUtil.limit(worldX, World.MAP_MIN_X, World.MAP_MAX_X) - World.MAP_MIN_X) >> 4; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe check -+ * Converts world Y to geodata Y. -+ * @param worldY -+ * @return int : Geo Y - */ -- public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) -+ public static int getGeoY(int worldY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return true; -- } -- return region.checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(worldY, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.MAP_MIN_Y) >> 4; - } - - /** -+ * Converts geodata X to world X. - * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe anti-corner cut -+ * @return int : World X - */ -- public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) -+ public static int getWorldX(int geoX) - { -- boolean can = true; -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- return can && checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(geoX, 0, GeoStructure.GEO_CELLS_X) << 4) + World.MAP_MIN_X + 8; - } - - /** -- * @param geoX -+ * Converts geodata Y to world Y. - * @param geoY -- * @param worldZ -- * @return the nearest Z value -+ * @return int : World Y - */ -- public int getNearestZ(int geoX, int geoY, int worldZ) -+ public static int getWorldY(int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return worldZ; -- } -- return region.getNearestZ(geoX, geoY, worldZ); -+ return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next lower Z value -+ * Returns block of geodata on given coordinates. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return {@link ABlock} : Block of geodata. - */ -- public int getNextLowerZ(int geoX, int geoY, int worldZ) -+ private final ABlock getBlock(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final int x = geoX / GeoStructure.BLOCK_CELLS_X; -+ final int y = geoY / GeoStructure.BLOCK_CELLS_Y; -+ -+ // if x or y is out of array return null -+ if ((x > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) - { -- return worldZ; -+ return _blocks[x][y]; - } -- return region.getNextLowerZ(geoX, geoY, worldZ); -+ return null; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next higher Z value -+ * Check if geo coordinates has geo. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return boolean : True, if given geo coordinates have geodata - */ -- public int getNextHigherZ(int geoX, int geoY, int worldZ) -+ public boolean hasGeoPos(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final ABlock block = getBlock(geoX, geoY); -+ if (block == null) // null block check - { -- return worldZ; -+ // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) -+ // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); -+ return false; - } -- return region.getNextHigherZ(geoX, geoY, worldZ); -+ return block.hasGeoPos(); - } - - /** -- * Gets the Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the height of cell, which is closest to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, closest to given coordinates. - */ -- public int getHeight(int x, int y, int z) -+ public short getHeightNearest(int geoX, int geoY, int worldZ) - { -- return getNearestZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getHeightNearest(geoX, geoY, worldZ) : (short) worldZ; - } - - /** -- * Gets the next lower Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the NSWE flag byte of cell, which is closes to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. - */ -- public int getLowerHeight(int x, int y, int z) -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) - { -- return getNextLowerZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getNsweNearest(geoX, geoY, worldZ) : (byte) 0xFF; - } - - /** -- * Gets the next higher Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Check if world coordinates has geo. -+ * @param worldX : World X -+ * @param worldY : World Y -+ * @return boolean : True, if given world coordinates have geodata - */ -- public int getHigherHeight(int x, int y, int z) -+ public boolean hasGeo(int worldX, int worldY) - { -- return getNextHigherZ(getGeoX(x), getGeoY(y), z); -+ return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); - } - - /** -- * @param worldX -- * @return the geo X -+ * Returns closest Z coordinate according to geodata. -+ * @param worldX : world x -+ * @param worldY : world y -+ * @param worldZ : world z -+ * @return short : nearest Z coordinates according to geodata - */ -- public int getGeoX(int worldX) -+ public short getHeight(int worldX, int worldY, int worldZ) - { -- return (worldX - WORLD_MIN_X) / 16; -+ return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - -- /** -- * @param worldY -- * @return the geo Y -- */ -- public int getGeoY(int worldY) -- { -- return (worldY - WORLD_MIN_Y) / 16; -- } -+ // PATHFINDING - - /** -- * @param worldZ -- * @return the geo Z -+ * Check line of sight from {@link WorldObject} to {@link WorldObject}. -+ * @param origin : The origin object. -+ * @param target : The target object. -+ * @return {@code boolean} : True if origin can see target - */ -- public int getGeoZ(int worldZ) -+ public boolean canSeeTarget(WorldObject origin, WorldObject target) - { -- return (worldZ - WORLD_MIN_Z) / 16; -- } -- -- /** -- * @param geoX -- * @return the world X -- */ -- public int getWorldX(int geoX) -- { -- return (geoX * 16) + WORLD_MIN_X + 8; -- } -- -- /** -- * @param geoY -- * @return the world Y -- */ -- public int getWorldY(int geoY) -- { -- return (geoY * 16) + WORLD_MIN_Y + 8; -- } -- -- /** -- * @param geoZ -- * @return the world Z -- */ -- public int getWorldZ(int geoZ) -- { -- return (geoZ * 16) + WORLD_MIN_Z + 8; -- } -- -- /** -- * Can see target. Doors as target always return true. Checks doors between. -- * @param cha the character -- * @param target the target -- * @return {@code true} if the character can see the target (LOS), {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, WorldObject target) -- { -- if (target.isDoor()) -+ if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) - { -- // Can always see doors. - return true; - } -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param cha the character -- * @param worldPosition the world position -- * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) -- { -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param world -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @param tworld the target's instanceId -- * @return -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) -- { -- return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instance -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) -- { -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) -+ -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = target.getX(); -+ final int ty = target.getY(); -+ final int tz = target.getZ(); -+ -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } -- return canSeeTarget(x, y, z, tx, ty, tz); -- } -- -- /** -- * @param prevX -- * @param prevY -- * @param prevGeoZ -- * @param curX -- * @param curY -- * @param nswe -- * @return the LOS Z value -- */ -- private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) -- { -- if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { -- throw new RuntimeException("Multiple directions!"); -+ return false; - } - -- if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- return getNearestZ(curX, curY, prevGeoZ); -+ return true; - } - -- return getNextHigherZ(curX, curY, prevGeoZ); -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) -+ { -+ return true; -+ } -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) -+ { -+ return goz == gtz; -+ } -+ -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) -+ { -+ oheight = ((Creature) origin).getCollisionHeight() * 2; -+ } -+ -+ double theight = 0; -+ if (target.isCreature()) -+ { -+ theight = ((Creature) target).getCollisionHeight() * 2; -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); - } - - /** -- * Can see target. Does not check doors between. -- * @param xValue the x coordinate -- * @param yValue the y coordinate -- * @param zValue the z coordinate -- * @param txValue the target's x coordinate -- * @param tyValue the target's y coordinate -- * @param tzValue the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -+ * Check line of sight from {@link WorldObject} to {@link Location}. -+ * @param origin : The origin object. -+ * @param position : The target position. -+ * @return {@code boolean} : True if object can see position - */ -- public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) -+ public boolean canSeeTarget(WorldObject origin, Location position) - { -- int x = xValue; -- int y = yValue; -- int tx = txValue; -- int ty = tyValue; -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = position.getX(); -+ final int ty = position.getY(); -+ final int tz = position.getZ(); - -- int geoX = getGeoX(x); -- int geoY = getGeoY(y); -- int tGeoX = getGeoX(tx); -- int tGeoY = getGeoY(ty); -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) -+ { -+ return false; -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) -+ { -+ return false; -+ } - -- int z = getNearestZ(geoX, geoY, zValue); -- int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- // Fastpath. -- if ((geoX == tGeoX) && (geoY == tGeoY)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- if (hasGeoPos(tGeoX, tGeoY)) -- { -- return z == tz; -- } - return true; - } - -- if (tz > z) -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) - { -- int tmp = tx; -- tx = x; -- x = tmp; -- -- tmp = ty; -- ty = y; -- y = tmp; -- -- tmp = tz; -- tz = z; -- z = tmp; -- -- tmp = tGeoX; -- tGeoX = geoX; -- geoX = tmp; -- -- tmp = tGeoY; -- tGeoY = geoY; -- geoY = tmp; -+ return goz == gtz; - } - -- final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); -- // First point is guaranteed to be available, skip it, we can always see our own position. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- final int prevZ = pointIter.z(); -- int prevGeoZ = prevZ; -- int ptIndex = 0; -- while (pointIter.next()) -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight(); -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); -+ } -+ -+ /** -+ * Simple check for origin to target visibility. -+ * @param goxValue : origin X geodata coordinate -+ * @param goyValue : origin Y geodata coordinate -+ * @param gozValue : origin Z geodata coordinate -+ * @param oheight : origin height (if instance of {@link Character}) -+ * @param gtxValue : target X geodata coordinate -+ * @param gtyValue : target Y geodata coordinate -+ * @param gtzValue : target Z geodata coordinate -+ * @param theight : target height (if instance of {@link Character}) -+ * @param instance -+ * @return {@code boolean} : True, when target can be seen. -+ */ -+ private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) -+ { -+ int goz = gozValue; -+ int gtz = gtzValue; -+ int gox = goxValue; -+ int goy = goyValue; -+ int gtx = gtxValue; -+ int gty = gtyValue; -+ -+ // get line of sight Z coordinates -+ double losoz = goz + ((oheight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ double lostz = gtz + ((theight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ -+ // get X delta and signum -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirox = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; -+ final byte dirtx = sx > 0 ? GeoStructure.CELL_FLAG_W : GeoStructure.CELL_FLAG_E; -+ -+ // get Y delta and signum -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte diroy = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ final byte dirty = sy > 0 ? GeoStructure.CELL_FLAG_N : GeoStructure.CELL_FLAG_S; -+ -+ // get Z delta -+ final int dm = Math.max(dx, dy); -+ final double dz = (lostz - losoz) / dm; -+ -+ // get direction flag for diagonal movement -+ final byte diroxy = getDirXY(dirox, diroy); -+ final byte dirtxy = getDirXY(dirtx, dirty); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte diro; -+ byte dirt; -+ -+ // initialize node values -+ int nox = gox; -+ int noy = goy; -+ int ntx = gtx; -+ int nty = gty; -+ byte nsweo = getNsweNearest(gox, goy, goz); -+ byte nswet = getNsweNearest(gtx, gty, gtz); -+ -+ // loop -+ ABlock block; -+ int index; -+ for (int i = 0; i < ((dm + 1) / 2); i++) -+ { -+ // reset direction flag -+ diro = 0; -+ dirt = 0; - -- if ((curX == prevX) && (curY == prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- continue; -+ // calculate next point XY coordinates -+ d -= dy; -+ d += dx; -+ nox += sx; -+ ntx -= sx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroxy; -+ dirt |= dirtxy; - } -+ else if (e2 > -dy) -+ { -+ // calculate next point X coordinate -+ d -= dy; -+ nox += sx; -+ ntx -= sx; -+ diro |= dirox; -+ dirt |= dirtx; -+ } -+ else if (e2 < dx) -+ { -+ // calculate next point Y coordinate -+ d += dx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroy; -+ dirt |= dirty; -+ } - -- final int beeCurZ = pointIter.z(); -- int curGeoZ = prevGeoZ; -- -- // Check if the position has geodata. -- if (hasGeoPos(curX, curY)) - { -- final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); -- curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); -- int maxHeight; -- if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) -+ // get block of the next cell -+ block = getBlock(nox, noy); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nsweo & diro) == 0) - { -- maxHeight = z + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); - } - else - { -- maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); - } - -- boolean canSeeThrough = false; -- if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) -+ // layer does not exist, return -+ if (index == -1) - { -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else -- { -- canSeeThrough = true; -- } -+ return false; - } - -- if (!canSeeThrough) -+ // get layer and next line of sight Z coordinate -+ goz = block.getHeight(index); -+ losoz += dz; -+ -+ // perform line of sight check, return when fails -+ if ((goz - losoz) > Config.MAX_OBSTACLE_HEIGHT) - { - return false; - } -+ -+ // get layer nswe -+ nsweo = block.getNswe(index); - } -+ { -+ // get block of the next cell -+ block = getBlock(ntx, nty); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nswet & dirt) == 0) -+ { -+ index = block.getIndexAbove(ntx, nty, gtz - GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ else -+ { -+ index = block.getIndexBelow(ntx, nty, gtz + GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ -+ // layer does not exist, return -+ if (index == -1) -+ { -+ return false; -+ } -+ -+ // get layer and next line of sight Z coordinate -+ gtz = block.getHeight(index); -+ lostz -= dz; -+ -+ // perform line of sight check, return when fails -+ if ((gtz - lostz) > Config.MAX_OBSTACLE_HEIGHT) -+ { -+ return false; -+ } -+ -+ // get layer nswe -+ nswet = block.getNswe(index); -+ } - -- prevX = curX; -- prevY = curY; -- prevGeoZ = curGeoZ; -- ++ptIndex; -+ // update coords -+ gox = nox; -+ goy = noy; -+ gtx = ntx; -+ gty = nty; - } - -- return true; -+ // when iteration is completed, compare final Z coordinates -+ return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); - } - - /** -- * Move check. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param zValue the z coordinate -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tzValue the target's z coordinate -- * @param instance the instance -- * @return the last Location (x,y,z) where player can walk - just before wall -+ * Check movement from coordinates to coordinates. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {code boolean} : True if target coordinates are reachable from origin coordinates - */ -- public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) -+ public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- final int geoX = getGeoX(x); -- final int geoY = getGeoY(y); -- final int z = getNearestZ(geoX, geoY, zValue); -- final int tGeoX = getGeoX(tx); -- final int tGeoY = getGeoY(ty); -- final int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } -- if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = z; -+ // perform geodata check -+ final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); -+ return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); -+ } -+ -+ /** -+ * Check movement from origin to target. Returns last available point in the checked path. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {@link Location} : Last point where object can walk (just before wall) -+ */ -+ public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) -+ { -+ return new Location(ox, oy, oz); -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) -+ { -+ return new Location(ox, oy, oz); -+ } - -- while (pointIter.next()) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -- -- if (hasGeoPos(prevX, prevY)) -- { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -- { -- // Can't move, return previous location. -- return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); -- } -- } -- -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return new Location(tx, ty, tz); - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != tz)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- // Different floors, return start location. -- return new Location(x, y, z); -+ return new Location(tx, ty, tz); - } - -- return new Location(tx, ty, tz); -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) -+ { -+ return new Location(tx, ty, tz); -+ } -+ -+ // perform geodata check -+ return checkMove(gox, goy, goz, gtx, gty, gtz, instance); - } - - /** -- * Checks if its possible to move from one location to another. -- * @param fromX the X coordinate to start checking from -- * @param fromY the Y coordinate to start checking from -- * @param fromZvalue the Z coordinate to start checking from -- * @param toX the X coordinate to end checking at -- * @param toY the Y coordinate to end checking at -- * @param toZvalue the Z coordinate to end checking at -- * @param instance the instance -- * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise -+ * With this method you can check if a position is visible or can be reached by beeline movement.
-+ * Target X and Y reachable and Z is on same floor: -+ *
    -+ *
  • Location of the target with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y reachable but Z is on another floor: -+ *
    -+ *
  • Location of the origin with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y not reachable: -+ *
    -+ *
  • Last accessible location in destination to target.
  • -+ *
-+ * @param gox : origin X geodata coordinate -+ * @param goy : origin Y geodata coordinate -+ * @param goz : origin Z geodata coordinate -+ * @param gtx : target X geodata coordinate -+ * @param gty : target Y geodata coordinate -+ * @param gtz : target Z geodata coordinate -+ * @param instance -+ * @return {@link GeoLocation} : The last allowed point of movement. - */ -- public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) -+ protected final GeoLocation checkMove(int gox, int goy, int goz, int gtx, int gty, int gtz, Instance instance) - { -- final int geoX = getGeoX(fromX); -- final int geoY = getGeoY(fromY); -- final int fromZ = getNearestZ(geoX, geoY, fromZvalue); -- final int tGeoX = getGeoX(toX); -- final int tGeoY = getGeoY(toY); -- final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); -- -- if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) -+ if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } -- if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) -+ if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = fromZ; -+ // get X delta, signum and direction flag -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirX = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; - -- while (pointIter.next()) -+ // get Y delta, signum and direction flag -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte dirY = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ -+ // get direction flag for diagonal movement -+ final byte dirXY = getDirXY(dirX, dirY); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte direction; -+ -+ // load pointer coordinates -+ int gpx = gox; -+ int gpy = goy; -+ int gpz = goz; -+ -+ // load next pointer -+ int nx = gpx; -+ int ny = gpy; -+ -+ // loop -+ int count = 0; -+ while (count++ < Config.MAX_ITERATIONS) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -+ direction = 0; - -- if (hasGeoPos(prevX, prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -+ d -= dy; -+ d += dx; -+ nx += sx; -+ ny += sy; -+ direction |= dirXY; -+ } -+ else if (e2 > -dy) -+ { -+ d -= dy; -+ nx += sx; -+ direction |= dirX; -+ } -+ else if (e2 < dx) -+ { -+ d += dx; -+ ny += sy; -+ direction |= dirY; -+ } -+ -+ // obstacle found, return -+ if ((getNsweNearest(gpx, gpy, gpz) & direction) == 0) -+ { -+ return new GeoLocation(gpx, gpy, gpz); -+ } -+ -+ // update pointer coordinates -+ gpx = nx; -+ gpy = ny; -+ gpz = getHeightNearest(nx, ny, gpz); -+ -+ // target coordinates reached -+ if ((gpx == gtx) && (gpy == gty)) -+ { -+ if (gpz == gtz) - { -- return false; -+ // path found, Z coordinates are okay, return target point -+ return new GeoLocation(gtx, gty, gtz); - } -+ -+ // path found, Z coordinates are not okay, return last good point -+ return new GeoLocation(gpx, gpy, gpz); - } -+ } -+ -+ return new GeoLocation(gox, goy, goz); -+ } -+ -+ /** -+ * Returns diagonal NSWE flag format of combined two NSWE flags. -+ * @param dirX : X direction NSWE flag -+ * @param dirY : Y direction NSWE flag -+ * @return byte : NSWE flag of combined direction -+ */ -+ private static byte getDirXY(byte dirX, byte dirY) -+ { -+ // check axis directions -+ if (dirY == GeoStructure.CELL_FLAG_N) -+ { -+ if (dirX == GeoStructure.CELL_FLAG_W) -+ { -+ return GeoStructure.CELL_FLAG_NW; -+ } - -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return GeoStructure.CELL_FLAG_NE; - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) -+ if (dirX == GeoStructure.CELL_FLAG_W) - { -- // Different floors. -- return false; -+ return GeoStructure.CELL_FLAG_SW; - } - -- return true; -+ return GeoStructure.CELL_FLAG_SE; - } - -+ /** -+ * Returns the list of location objects as a result of complete path calculation. -+ * @param ox : origin x -+ * @param oy : origin y -+ * @param oz : origin z -+ * @param tx : target x -+ * @param ty : target y -+ * @param tz : target z -+ * @param instance -+ * @return {@code List} : complete path from nodes -+ */ -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ return null; -+ } -+ -+ /** -+ * Returns the instance of the {@link GeoEngine}. -+ * @return {@link GeoEngine} : The instance. -+ */ - public static GeoEngine getInstance() - { - return SingletonHolder.INSTANCE; -@@ -743,6 +934,6 @@ - - private static class SingletonHolder - { -- protected static final GeoEngine INSTANCE = new GeoEngine(); -+ protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); - } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (working copy) -@@ -20,88 +20,84 @@ - import java.util.LinkedList; - import java.util.List; - import java.util.ListIterator; --import java.util.logging.Level; --import java.util.logging.Logger; - - import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; --import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; --import org.l2jmobius.gameserver.model.World; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -+import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.instancezone.Instance; - - /** -- * @author -Nemesiss- -+ * @author Hasha - */ --public class GeoEnginePathfinding -+final class GeoEnginePathfinding extends GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); -+ // pre-allocated buffers -+ private final BufferHolder[] _buffers; - -- private BufferInfo[] _buffers; -- - protected GeoEnginePathfinding() - { -- try -+ super(); -+ -+ final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ _buffers = new BufferHolder[array.length]; -+ int count = 0; -+ for (int i = 0; i < array.length; i++) - { -- final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ final String buf = array[i]; -+ final String[] args = buf.split("x"); - -- _buffers = new BufferInfo[array.length]; -- -- String buf; -- String[] args; -- for (int i = 0; i < array.length; i++) -+ try - { -- buf = array[i]; -- args = buf.split("x"); -- if (args.length != 2) -- { -- throw new Exception("Invalid buffer definition: " + buf); -- } -- -- _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); -+ final int size = Integer.parseInt(args[1]); -+ count += size; -+ _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); - } -+ catch (Exception e) -+ { -+ LOGGER.warning("GeoEnginePathfinding: Can not load buffer setting: " + buf); -+ } - } -- catch (Exception e) -- { -- LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); -- throw new Error("CellPathFinding: load aborted"); -- } -+ -+ LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); - } - -- public boolean pathNodesExist(short regionoffset) -+ @Override -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- return false; -- } -- -- public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) -- { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -- if (!GeoEngine.getInstance().hasGeo(x, y)) -+ // get origin and check existing geo coords -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { - 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)) -+ -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coords -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { - 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)))); -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // Prepare buffer for pathfinding calculations -+ final NodeBuffer buffer = getBuffer(64 + (2 * Math.max(Math.abs(gox - gtx), Math.abs(goy - gty)))); - if (buffer == null) - { - return null; - } - -- List path = null; -+ // find path -+ List path = null; - try - { -- final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); -- -+ final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); - if (result == null) - { - return null; -@@ -125,33 +121,45 @@ - return path; - } - -- int currentX, currentY, currentZ; -- ListIterator middlePoint; -+ // get path list iterator -+ final ListIterator point = path.listIterator(); - -- middlePoint = path.listIterator(); -- currentX = x; -- currentY = y; -- currentZ = z; -+ // get node A (origin) -+ int nodeAx = gox; -+ int nodeAy = goy; -+ short nodeAz = goz; - -- while (middlePoint.hasNext()) -+ // get node B -+ GeoLocation nodeB = (GeoLocation) point.next(); -+ -+ // iterate thought the path to optimize it -+ int count = 0; -+ while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) - { -- final AbstractNodeLoc locMiddle = middlePoint.next(); -- if (!middlePoint.hasNext()) -- { -- break; -- } -+ // get node C -+ final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - -- final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); -- if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) -+ // check movement from node A to node C -+ final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); -+ if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) - { -- middlePoint.remove(); -+ // can move from node A to node C -+ -+ // remove node B -+ point.remove(); - } - else - { -- currentX = locMiddle.getX(); -- currentY = locMiddle.getY(); -- currentZ = locMiddle.getZ(); -+ // can not move from node A to node C -+ -+ // set node A (node B is part of path, update A coordinates) -+ nodeAx = nodeB.getGeoX(); -+ nodeAy = nodeB.getGeoY(); -+ nodeAz = (short) nodeB.getZ(); - } -+ -+ // set node B -+ nodeB = (GeoLocation) point.next(); - } - - return path; -@@ -158,144 +166,102 @@ - } - - /** -- * Convert geodata position to pathnode position -- * @param geo_pos -- * @return pathnode position -+ * Create list of node locations as result of calculated buffer node tree. -+ * @param node : the entry point -+ * @return List : list of node location - */ -- public short getNodePos(int geo_pos) -+ private static List constructPath(Node node) - { -- return (short) (geo_pos >> 3); // OK? -- } -- -- /** -- * Convert node position to pathnode block position -- * @param node_pos -- * @return pathnode block position (0...255) -- */ -- public short getNodeBlock(int node_pos) -- { -- return (short) (node_pos % 256); -- } -- -- public byte getRegionX(int node_pos) -- { -- return (byte) ((node_pos >> 8) + World.TILE_X_MIN); -- } -- -- public byte getRegionY(int node_pos) -- { -- return (byte) ((node_pos >> 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 node_x rx -- * @return -- */ -- public int calculateWorldX(short node_x) -- { -- return World.MAP_MIN_X + (node_x * 128) + 48; -- } -- -- /** -- * Convert pathnode y to World y position -- * @param node_y -- * @return -- */ -- public int calculateWorldY(short node_y) -- { -- return World.MAP_MIN_Y + (node_y * 128) + 48; -- } -- -- private List constructPath(AbstractNode nodeValue) -- { -- final LinkedList path = new LinkedList<>(); -- int previousDirectionX = Integer.MIN_VALUE; -- int previousDirectionY = Integer.MIN_VALUE; -- int directionX, directionY; -+ // create empty list -+ final LinkedList list = new LinkedList<>(); - -- AbstractNode node = nodeValue; -- while (node.getParent() != null) -+ // set direction X/Y -+ int dx = 0; -+ int dy = 0; -+ -+ // get target parent -+ Node target = node; -+ Node parent = target.getParent(); -+ -+ // while parent exists -+ int count = 0; -+ while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { -- directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); -- directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); -+ // get parent <> target direction X/Y -+ final int nx = parent.getLoc().getGeoX() - target.getLoc().getGeoX(); -+ final int ny = parent.getLoc().getGeoY() - target.getLoc().getGeoY(); - -- // only add a new route point if moving direction changes -- if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) -+ // direction has changed? -+ if ((dx != nx) || (dy != ny)) - { -- previousDirectionX = directionX; -- previousDirectionY = directionY; -+ // add node to the beginning of the list -+ list.addFirst(target.getLoc()); - -- path.addFirst(node.getLoc()); -- node.setLoc(null); -+ // update direction X/Y -+ dx = nx; -+ dy = ny; - } - -- node = node.getParent(); -+ // move to next node, set target and get its parent -+ target = parent; -+ parent = target.getParent(); - } - -- return path; -+ // return list -+ return list; - } - -- private CellNodeBuffer alloc(int size) -+ /** -+ * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer. -+ * @param size : pre-calculated minimal required size -+ * @return NodeBuffer : buffer -+ */ -+ private final NodeBuffer getBuffer(int size) - { -- CellNodeBuffer current = null; -- for (BufferInfo i : _buffers) -+ NodeBuffer current = null; -+ for (BufferHolder holder : _buffers) - { -- if (i.mapSize >= size) -+ // Find proper size of buffer -+ if (holder._size < size) - { -- for (CellNodeBuffer buf : i.buffer) -+ continue; -+ } -+ -+ // Find unlocked NodeBuffer -+ for (NodeBuffer buffer : holder._buffer) -+ { -+ if (!buffer.isLocked()) - { -- if (buf.lock()) -- { -- current = buf; -- break; -- } -+ continue; - } -- if (current != null) -- { -- break; -- } - -- // not found, allocate temporary buffer -- current = new CellNodeBuffer(i.mapSize); -- current.lock(); -- if (i.buffer.size() < i.count) -- { -- i.buffer.add(current); -- break; -- } -+ return buffer; - } -+ -+ // NodeBuffer not found, allocate temporary buffer -+ current = new NodeBuffer(holder._size); -+ current.isLocked(); - } - - return current; - } - -- private static final class BufferInfo -+ /** -+ * NodeBuffer container with specified size and count of separate buffers. -+ */ -+ private static final class BufferHolder - { -- final int mapSize; -- final int count; -- ArrayList buffer; -+ final int _size; -+ List _buffer; - -- public BufferInfo(int size, int cnt) -+ public BufferHolder(int size, int count) - { -- mapSize = size; -- count = cnt; -- buffer = new ArrayList<>(count); -+ _size = size; -+ _buffer = new ArrayList<>(count); -+ for (int i = 0; i < count; i++) -+ { -+ _buffer.add(new NodeBuffer(size)); -+ } - } - } -- -- public static GeoEnginePathfinding getInstance() -- { -- return SingletonHolder.INSTANCE; -- } -- -- private static class SingletonHolder -- { -- protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); -- } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (working copy) -@@ -0,0 +1,197 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @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 height of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getHeightNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first above given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, above given coordinates. -+ */ -+ public abstract short getHeightAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first below given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is closest to given coordinates. -+ * @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 the NSWE flag byte of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getNsweNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first above given coordinates. -+ * @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 getNsweAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first below given coordinates. -+ * @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 getNsweBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is closes layer to given coordinates. -+ * @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. -+ * @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 above given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is first layer below given coordinates. -+ * @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 index to data of the cell, which is first layer below given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexBelowOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeight(int index); -+ -+ /** -+ * Returns the height of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightOriginal(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNswe(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNsweOriginal(int index); -+ -+ /** -+ * Sets the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @param nswe : New NSWE flag byte. -+ */ -+ public abstract void setNswe(int index, byte nswe); -+ -+ /** -+ * Saves the block in L2D format to {@link BufferedOutputStream}. Used only for L2D geodata conversion. -+ * @param stream : The stream. -+ * @throws IOException : Can't save the block to steam. -+ */ -+ public abstract void saveBlock(BufferedOutputStream stream) throws IOException; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (working copy) -@@ -0,0 +1,252 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockComplex(ByteBuffer bb, GeoFormat format) -+ { -+ // initialize buffer -+ _buffer = new byte[GeoStructure.BLOCK_CELLS * 3]; -+ -+ // load data -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // 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); -+ } -+ else -+ { -+ // get nswe -+ final byte nswe = bb.get(); -+ _buffer[i * 3] = nswe; -+ -+ // get height -+ final short height = bb.getShort(); -+ _buffer[(i * 3) + 1] = (byte) (height & 0x00FF); -+ _buffer[(i * 3) + 2] = (byte) (height >> 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height > worldZ ? height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height < worldZ ? height : Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public 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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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 int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_COMPLEX_L2D); -+ -+ // write block data -+ stream.write(_buffer, 0, GeoStructure.BLOCK_CELLS * 3); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (working copy) -@@ -0,0 +1,176 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockFlat extends ABlock -+{ -+ protected final short _height; -+ protected byte _nswe; -+ -+ /** -+ * Creates FlatBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockFlat(ByteBuffer bb, GeoFormat format) -+ { -+ _height = bb.getShort(); -+ _nswe = format != GeoFormat.L2D ? 0x0F : (byte) (0xFF); -+ if (format == GeoFormat.L2OFF) -+ { -+ bb.getShort(); -+ } -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return true; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height > worldZ ? _height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height < worldZ ? _height : Short.MAX_VALUE; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height > worldZ ? _nswe : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height < worldZ ? _nswe : 0; -+ } -+ -+ @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return index -+ return _height < worldZ ? 0 : -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _nswe = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_FLAT_L2D); -+ -+ // write height -+ stream.write((byte) (_height & 0x00FF)); -+ stream.write((byte) (_height >> 8)); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (working copy) -@@ -0,0 +1,465 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+import java.nio.ByteOrder; -+import java.util.Arrays; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockMultilayer extends ABlock -+{ -+ private static final int MAX_LAYERS = Byte.MAX_VALUE; -+ -+ private static ByteBuffer _temp; -+ -+ /** -+ * Initializes the temporarily buffer. -+ */ -+ public static void initialize() -+ { -+ // initialize temporarily buffer and sorting mechanism -+ _temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3); -+ _temp.order(ByteOrder.LITTLE_ENDIAN); -+ } -+ -+ /** -+ * Releases temporarily buffer. -+ */ -+ public static void release() -+ { -+ _temp = null; -+ } -+ -+ protected byte[] _buffer; -+ -+ /** -+ * Implicit constructor for children class. -+ */ -+ protected BlockMultilayer() -+ { -+ _buffer = null; -+ } -+ -+ /** -+ * Creates MultilayerBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockMultilayer(ByteBuffer bb, GeoFormat format) -+ { -+ // 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 = format != GeoFormat.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++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // get data -+ final short data = bb.getShort(); -+ -+ // add nswe and height -+ _temp.put((byte) (data & 0x000F)); -+ _temp.putShort((short) ((short) (data & 0xFFF0) >> 1)); -+ } -+ else -+ { -+ // add nswe -+ _temp.put(bb.get()); -+ -+ // add height -+ _temp.putShort(bb.getShort()); -+ } -+ } -+ } -+ -+ // 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 height -+ if (height > worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, return minimum value -+ return Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 height -+ if (height < worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, return maximum value -+ return Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 nswe -+ if (height > worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 nswe -+ if (height < worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @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 first layer data (first from bottom) -+ byte layers = _buffer[index++]; -+ -+ // loop though all cell layers, find closest layer -+ 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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ // get height -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(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]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ // get nswe -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ // set nswe -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_MULTILAYER_L2D); -+ -+ // for each cell -+ int index = 0; -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ // write layers count -+ final byte layers = _buffer[index++]; -+ stream.write(layers); -+ -+ // write cell data -+ stream.write(_buffer, index, layers * 3); -+ -+ // move index to next cell -+ index += layers * 3; -+ } -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (working copy) -@@ -0,0 +1,150 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockNull extends ABlock -+{ -+ private final byte _nswe; -+ -+ public BlockNull() -+ { -+ _nswe = (byte) 0xFF; -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return false; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweBelow(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) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) -+ { -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (nonexistent) -@@ -1,48 +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 . -- */ --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() -- { -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (nonexistent) -@@ -1,77 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (nonexistent) -@@ -1,56 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (working copy) -@@ -0,0 +1,39 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public enum GeoFormat -+{ -+ L2J("%d_%d.l2j"), -+ L2OFF("%d_%d_conv.dat"), -+ L2D("%d_%d.l2d"); -+ -+ private final String _filename; -+ -+ private GeoFormat(String filename) -+ { -+ _filename = filename; -+ } -+ -+ public String getFilename() -+ { -+ return _filename; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (working copy) -@@ -0,0 +1,67 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.geoengine.GeoEngine; -+import org.l2jmobius.gameserver.model.Location; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoLocation extends Location -+{ -+ private byte _nswe; -+ -+ public GeoLocation(int x, int y, int z) -+ { -+ super(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public void set(int x, int y, short z) -+ { -+ super.setXYZ(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public int getGeoX() -+ { -+ return _x; -+ } -+ -+ public int getGeoY() -+ { -+ return _y; -+ } -+ -+ @Override -+ public int getX() -+ { -+ return GeoEngine.getWorldX(_x); -+ } -+ -+ @Override -+ public int getY() -+ { -+ return GeoEngine.getWorldY(_y); -+ } -+ -+ public byte getNSWE() -+ { -+ return _nswe; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (working copy) -@@ -0,0 +1,70 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoStructure -+{ -+ // cells -+ public static final byte CELL_FLAG_E = 1 << 0; -+ public static final byte CELL_FLAG_W = 1 << 1; -+ public static final byte CELL_FLAG_S = 1 << 2; -+ public static final byte CELL_FLAG_N = 1 << 3; -+ public static final byte CELL_FLAG_SE = 1 << 4; -+ public static final byte CELL_FLAG_SW = 1 << 5; -+ public static final byte CELL_FLAG_NE = 1 << 6; -+ public static final byte CELL_FLAG_NW = (byte) (1 << 7); -+ -+ public static final int CELL_HEIGHT = 8; -+ public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6; -+ -+ // blocks -+ public static final byte TYPE_FLAT_L2J_L2OFF = 0; -+ public static final byte TYPE_FLAT_L2D = (byte) 0xD0; -+ public static final byte TYPE_COMPLEX_L2J = 1; -+ public static final byte TYPE_COMPLEX_L2OFF = 0x40; -+ public static final byte TYPE_COMPLEX_L2D = (byte) 0xD1; -+ 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) -+ public static final byte TYPE_MULTILAYER_L2D = (byte) 0xD2; -+ -+ 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; -+ -+ // regions -+ 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; -+ -+ // global geodata -+ private static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); -+ private 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; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (nonexistent) -@@ -1,42 +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 . -- */ --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); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (working copy) -@@ -0,0 +1,53 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public interface IGeoObject -+{ -+ /** -+ * Returns geodata X coordinate of the {@link IGeoObject}. -+ * @return int : Geodata X coordinate. -+ */ -+ int getGeoX(); -+ -+ /** -+ * Returns geodata Y coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Y coordinate. -+ */ -+ int getGeoY(); -+ -+ /** -+ * Returns geodata Z coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Z coordinate. -+ */ -+ int getGeoZ(); -+ -+ /** -+ * Returns height of the {@link IGeoObject}. -+ * @return int : Height. -+ */ -+ int getHeight(); -+ -+ /** -+ * Returns {@link IGeoObject} data. -+ * @return byte[][] : {@link IGeoObject} data. -+ */ -+ byte[][] getObjectGeoData(); -+} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (nonexistent) -@@ -1,47 +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 . -- */ --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(); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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); -- -- // 1 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (nonexistent) -@@ -1,55 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --/** -- * @author HorridoJoho -- */ --public final class NullRegion implements IRegion --{ -- public static final NullRegion INSTANCE = new NullRegion(); -- -- @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 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Region.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (nonexistent) -@@ -1,92 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (nonexistent) -@@ -1,87 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public abstract class AbstractNode --{ -- private T _loc; -- private AbstractNode _parent; -- -- public AbstractNode(T loc) -- { -- _loc = loc; -- } -- -- public void setParent(AbstractNode p) -- { -- _parent = p; -- } -- -- public AbstractNode getParent() -- { -- return _parent; -- } -- -- public T getLoc() -- { -- return _loc; -- } -- -- public void setLoc(T l) -- { -- _loc = l; -- } -- -- @Override -- public int hashCode() -- { -- final int prime = 31; -- int result = 1; -- result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); -- return result; -- } -- -- @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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (nonexistent) -@@ -1,33 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --/** -- * @author -Nemesiss- -- */ --public abstract class AbstractNodeLoc --{ -- public abstract int getX(); -- -- public abstract int getY(); -- -- public abstract int getZ(); -- -- public abstract int getNodeX(); -- -- public abstract int getNodeY(); --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (nonexistent) -@@ -1,67 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public class CellNode extends AbstractNode --{ -- 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (nonexistent) -@@ -1,348 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --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 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) -- { -- _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(); -- } -- -- public final List debugPath() -- { -- final List 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 void getNeighbors() -- { -- if (!_current.getLoc().canGoAll()) -- { -- 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); -- } -- -- // SouthEast -- if ((nodeE != null) && (nodeS != null)) -- { -- if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) -- { -- addNode(x + 1, y + 1, z, true); -- } -- } -- -- // SouthWest -- if ((nodeS != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) -- { -- addNode(x - 1, y + 1, z, true); -- } -- } -- -- // NorthEast -- if ((nodeN != null) && (nodeE != null)) -- { -- if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) -- { -- addNode(x + 1, y - 1, z, true); -- } -- } -- -- // NorthWest -- if ((nodeN != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) -- { -- addNode(x - 1, y - 1, z, true); -- } -- } -- } -- -- private 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(); -- // reinit node if needed -- if (result.getLoc() != null) -- { -- result.getLoc().set(x, y, z); -- } -- else -- { -- result.setLoc(new NodeLoc(x, y, z)); -- } -- } -- -- return result; -- } -- -- private 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 boolean isHighWeight(int x, int y, int z) -- { -- final CellNode result = getNode(x, y, z); -- if (result == null) -- { -- return true; -- } -- -- if (!result.getLoc().canGoAll()) -- { -- return true; -- } -- if (Math.abs(result.getLoc().getZ() - z) > 16) -- { -- return true; -- } -- -- return false; -- } -- -- private 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (working copy) -@@ -0,0 +1,87 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+ -+/** -+ * @author Hasha -+ */ -+public class Node -+{ -+ // node coords and nswe flag -+ private GeoLocation _loc; -+ -+ // node parent (for reverse path construction) -+ private Node _parent; -+ // node child (for moving over nodes during iteration) -+ private Node _child; -+ -+ // node G cost (movement cost = parent movement cost + current movement cost) -+ private double _cost = -1000; -+ -+ public void setLoc(int x, int y, int z) -+ { -+ _loc = new GeoLocation(x, y, z); -+ } -+ -+ public GeoLocation getLoc() -+ { -+ return _loc; -+ } -+ -+ public void setParent(Node parent) -+ { -+ _parent = parent; -+ } -+ -+ public Node getParent() -+ { -+ return _parent; -+ } -+ -+ public void setChild(Node child) -+ { -+ _child = child; -+ } -+ -+ public Node getChild() -+ { -+ return _child; -+ } -+ -+ public void setCost(double cost) -+ { -+ _cost = cost; -+ } -+ -+ public double getCost() -+ { -+ return _cost; -+ } -+ -+ public void free() -+ { -+ // reset node location -+ _loc = null; -+ -+ // reset node parent, child and cost -+ _parent = null; -+ _child = null; -+ _cost = -1000; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (working copy) -@@ -0,0 +1,306 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import java.util.concurrent.locks.ReentrantLock; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+ -+/** -+ * @author DS, Hasha; Credits to Diamond -+ */ -+public class NodeBuffer -+{ -+ private final ReentrantLock _lock = new ReentrantLock(); -+ private final int _size; -+ private final Node[][] _buffer; -+ -+ // center coordinates -+ private int _cx = 0; -+ private int _cy = 0; -+ -+ // target coordinates -+ private int _gtx = 0; -+ private int _gty = 0; -+ private short _gtz = 0; -+ -+ private Node _current = null; -+ -+ /** -+ * Constructor of NodeBuffer. -+ * @param size : one dimension size of buffer -+ */ -+ public NodeBuffer(int size) -+ { -+ // set size -+ _size = size; -+ -+ // initialize buffer -+ _buffer = new Node[size][size]; -+ for (int x = 0; x < size; x++) -+ { -+ for (int y = 0; y < size; y++) -+ { -+ _buffer[x][y] = 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 Node : first node of path -+ */ -+ public Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) -+ { -+ // set coordinates (middle of the line (gox,goy) - (gtx,gty), will be in the center of the buffer) -+ _cx = gox + ((gtx - gox - _size) / 2); -+ _cy = goy + ((gty - goy - _size) / 2); -+ -+ _gtx = gtx; -+ _gty = gty; -+ _gtz = gtz; -+ -+ _current = getNode(gox, goy, goz); -+ _current.setCost(getCostH(gox, goy, goz)); -+ -+ int count = 0; -+ do -+ { -+ // reached target? -+ if ((_current.getLoc().getGeoX() == _gtx) && (_current.getLoc().getGeoY() == _gty) && (Math.abs(_current.getLoc().getZ() - _gtz) < 8)) -+ { -+ return _current; -+ } -+ -+ // expand current node -+ expand(); -+ -+ // move pointer -+ _current = _current.getChild(); -+ } -+ while ((_current != null) && (count++ < Config.MAX_ITERATIONS)); -+ -+ return null; -+ } -+ -+ public boolean isLocked() -+ { -+ return _lock.tryLock(); -+ } -+ -+ public void free() -+ { -+ _current = null; -+ -+ for (Node[] nodes : _buffer) -+ { -+ for (Node node : nodes) -+ { -+ if (node.getLoc() != null) -+ { -+ node.free(); -+ } -+ } -+ } -+ -+ _lock.unlock(); -+ } -+ -+ /** -+ * Check _current Node and add its neighbors to the buffer. -+ */ -+ private final void expand() -+ { -+ // can't move anywhere, don't expand -+ final byte nswe = _current.getLoc().getNSWE(); -+ if (nswe == 0) -+ { -+ return; -+ } -+ -+ // get geo coords of the node to be expanded -+ final int x = _current.getLoc().getGeoX(); -+ final int y = _current.getLoc().getGeoY(); -+ final short z = (short) _current.getLoc().getZ(); -+ -+ // can move north, expand -+ if ((nswe & GeoStructure.CELL_FLAG_N) != 0) -+ { -+ addNode(x, y - 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move south, expand -+ if ((nswe & GeoStructure.CELL_FLAG_S) != 0) -+ { -+ addNode(x, y + 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_W) != 0) -+ { -+ addNode(x - 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_E) != 0) -+ { -+ addNode(x + 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move north-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NW) != 0) -+ { -+ addNode(x - 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move north-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NE) != 0) -+ { -+ addNode(x + 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SW) != 0) -+ { -+ addNode(x - 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SE) != 0) -+ { -+ addNode(x + 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ } -+ -+ /** -+ * Returns node, if it exists in buffer. -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param z : node Z coord -+ * @return Node : node, if exits in buffer -+ */ -+ private final Node getNode(int x, int y, short z) -+ { -+ // check node X out of coordinates -+ final int ix = x - _cx; -+ if ((ix < 0) || (ix >= _size)) -+ { -+ return null; -+ } -+ -+ // check node Y out of coordinates -+ final int iy = y - _cy; -+ if ((iy < 0) || (iy >= _size)) -+ { -+ return null; -+ } -+ -+ // get node -+ final Node result = _buffer[ix][iy]; -+ -+ // check and update -+ if (result.getLoc() == null) -+ { -+ result.setLoc(x, y, z); -+ } -+ -+ // return node -+ return result; -+ } -+ -+ /** -+ * Add node given by coordinates to the buffer. -+ * @param x : geo X coord -+ * @param y : geo Y coord -+ * @param z : geo Z coord -+ * @param weight : weight of movement to new node -+ */ -+ private final void addNode(int x, int y, short z, int weight) -+ { -+ // get node to be expanded -+ final Node node = getNode(x, y, z); -+ if (node == null) -+ { -+ return; -+ } -+ -+ // Z distance between nearby cells is higher than cell size -+ if (node.getLoc().getZ() > (z + (2 * GeoStructure.CELL_HEIGHT))) -+ { -+ return; -+ } -+ -+ // node was already expanded, return -+ if (node.getCost() >= 0) -+ { -+ return; -+ } -+ -+ node.setParent(_current); -+ if (node.getLoc().getNSWE() != (byte) 0xFF) -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + (weight * Config.OBSTACLE_MULTIPLIER)); -+ } -+ else -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + weight); -+ } -+ -+ Node current = _current; -+ int count = 0; -+ while ((current.getChild() != null) && (count < (Config.MAX_ITERATIONS * 4))) -+ { -+ count++; -+ if (current.getChild().getCost() > node.getCost()) -+ { -+ node.setChild(current.getChild()); -+ break; -+ } -+ current = current.getChild(); -+ } -+ -+ if (count >= (Config.MAX_ITERATIONS * 4)) -+ { -+ System.err.println("Pathfinding: too long loop detected, cost:" + node.getCost()); -+ } -+ -+ current.setChild(node); -+ } -+ -+ /** -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param i : node Z coord -+ * @return double : node cost -+ */ -+ private final double getCostH(int x, int y, int i) -+ { -+ final int dX = x - _gtx; -+ final int dY = y - _gty; -+ final int dZ = (i - _gtz) / GeoStructure.CELL_HEIGHT; -+ -+ // return (Math.abs(dX) + Math.abs(dY) + Math.abs(dZ)) * Config.HEURISTIC_WEIGHT; // Manhattan distance -+ return Math.sqrt((dX * dX) + (dY * dY) + (dZ * dZ)) * Config.HEURISTIC_WEIGHT; // Direct distance -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -- --/** -- * @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 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 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; -- // return super.hashCode(); -- } -- -- @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; -- if (_x != other._x) -- { -- return false; -- } -- if (_y != other._y) -- { -- return false; -- } -- if (_goNorth != other._goNorth) -- { -- return false; -- } -- if (_goEast != other._goEast) -- { -- return false; -- } -- if (_goSouth != other._goSouth) -- { -- return false; -- } -- if (_goWest != other._goWest) -- { -- return false; -- } -- if (_geoHeight != other._geoHeight) -- { -- return false; -- } -- return true; -- } --} -Index: java/org/l2jmobius/gameserver/model/actor/Creature.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/Creature.java (revision 8428) -+++ java/org/l2jmobius/gameserver/model/actor/Creature.java (working copy) -@@ -65,8 +65,6 @@ - import org.l2jmobius.gameserver.enums.TeleportWhereType; - import org.l2jmobius.gameserver.enums.UserInfoType; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; - import org.l2jmobius.gameserver.instancemanager.IdManager; - import org.l2jmobius.gameserver.instancemanager.MapRegionManager; - import org.l2jmobius.gameserver.instancemanager.QuestManager; -@@ -2566,7 +2564,7 @@ - - public boolean disregardingGeodata; - public int onGeodataPathIndex; -- public List geoPath; -+ public List geoPath; - public int geoPathAccurateTx; - public int geoPathAccurateTy; - public int geoPathGtx; -@@ -3443,7 +3441,7 @@ - if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) - { - // Path calculation -- overrides previous movement check -- m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); -+ m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); - if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found - { - if (isPlayer() && !_isFlying && !isInWater) -Index: java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (revision 8424) -+++ java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (working copy) -@@ -12746,7 +12746,7 @@ - { - if (Config.CORRECT_PLAYER_Z) - { -- final int nearestZ = GeoEngine.getInstance().getHigherHeight(getX(), getY(), getZ()); -+ final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); - if (getZ() < nearestZ) - { - teleToLocation(new Location(getX(), getY(), nearestZ)); -Index: java/org/l2jmobius/gameserver/util/GeoUtils.java -=================================================================== ---- java/org/l2jmobius/gameserver/util/GeoUtils.java (revision 8409) -+++ java/org/l2jmobius/gameserver/util/GeoUtils.java (working copy) -@@ -19,7 +19,7 @@ - import java.awt.Color; - - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; - import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; - -@@ -26,25 +26,25 @@ - /** - * @author HorridoJoho - */ --public final class GeoUtils -+public class GeoUtils - { - public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); - - final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); - - while (iter.next()) - { -- final int wx = GeoEngine.getInstance().getWorldX(iter.x()); -- final int wy = GeoEngine.getInstance().getWorldY(iter.y()); -+ final int wx = GeoEngine.getWorldX(iter.x()); -+ final int wy = GeoEngine.getWorldY(iter.y()); - - prim.addPoint(Color.RED, wx, wy, z); - } -@@ -53,21 +53,21 @@ - - public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); -- int wy = GeoEngine.getInstance().getWorldY(prevY); -+ int wx = GeoEngine.getWorldX(prevX); -+ int wy = GeoEngine.getWorldY(prevY); - int wz = iter.z(); - prim.addPoint(Color.RED, wx, wy, wz); - -@@ -78,8 +78,8 @@ - - if ((curX != prevX) || (curY != prevY)) - { -- wx = GeoEngine.getInstance().getWorldX(curX); -- wy = GeoEngine.getInstance().getWorldY(curY); -+ wx = GeoEngine.getWorldX(curX); -+ wy = GeoEngine.getWorldY(curY); - wz = iter.z(); - - prim.addPoint(Color.RED, wx, wy, wz); -@@ -93,7 +93,7 @@ - - private static Color getDirectionColor(int x, int y, int z, int nswe) - { -- if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) -+ if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) - { - return Color.GREEN; - } -@@ -109,9 +109,8 @@ - int iPacket = 0; - - ExServerPrimitive exsp = null; -- final GeoEngine ge = GeoEngine.getInstance(); -- final int playerGx = ge.getGeoX(player.getX()); -- final int playerGy = ge.getGeoY(player.getY()); -+ final int playerGx = GeoEngine.getGeoX(player.getX()); -+ final int playerGy = GeoEngine.getGeoY(player.getY()); - for (int dx = -geoRadius; dx <= geoRadius; ++dx) - { - for (int dy = -geoRadius; dy <= geoRadius; ++dy) -@@ -135,12 +134,12 @@ - final int gx = playerGx + dx; - final int gy = playerGy + dy; - -- final int x = ge.getWorldX(gx); -- final int y = ge.getWorldY(gy); -- final int z = ge.getNearestZ(gx, gy, player.getZ()); -+ final int x = GeoEngine.getWorldX(gx); -+ final int y = GeoEngine.getWorldY(gy); -+ final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); - - // north arrow -- Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); -+ Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); - 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); -@@ -147,7 +146,7 @@ - exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z); - - // east arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); - 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); -@@ -154,13 +153,13 @@ - exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z); - - // south arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); - 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, Cell.NSWE_WEST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); - 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); -@@ -188,15 +187,15 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_EAST; -+ return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_EAST; -+ return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; - } - else - { -- return Cell.NSWE_EAST; -+ return GeoStructure.CELL_FLAG_E; // Direction.EAST; - } - } - else if (x < lastX) // west -@@ -203,26 +202,27 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_WEST; -+ return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_WEST; -+ return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; - } - else - { -- return Cell.NSWE_WEST; -+ return GeoStructure.CELL_FLAG_W; // Direction.WEST; - } - } -- else // unchanged x -+ else -+ // unchanged x - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH; -+ return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH; -+ return GeoStructure.CELL_FLAG_N; // Direction.NORTH; - } - else - { -Index: java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java -=================================================================== ---- java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (nonexistent) -+++ java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (working copy) -@@ -0,0 +1,386 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.tools.geodataconverter; -+ -+import java.io.BufferedOutputStream; -+import java.io.File; -+import java.io.FileOutputStream; -+import java.io.RandomAccessFile; -+import java.nio.ByteOrder; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.Scanner; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.commons.util.PropertiesParser; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoDataConverter -+{ -+ private static GeoFormat _format; -+ private static ABlock[][] _blocks; -+ -+ public static void main(String[] args) -+ { -+ // initialize config -+ loadGeoengineConfigs(); -+ -+ // get geodata format -+ String type = ""; -+ try (Scanner scn = new Scanner(System.in)) -+ { -+ while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) -+ { -+ System.out.println("GeoDataConverter: Select source geodata type:"); -+ System.out.println(" J: L2J (e.g. 23_22.l2j)"); -+ System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); -+ System.out.println(" E: Exit"); -+ System.out.print("Choice: "); -+ type = scn.next(); -+ } -+ } -+ if (type.equalsIgnoreCase("E")) -+ { -+ System.exit(0); -+ } -+ -+ _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; -+ -+ // start conversion -+ System.out.println("GeoDataConverter: Converting all " + _format + " files."); -+ -+ // initialize geodata container -+ _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files -+ int converted = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) -+ { -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) -+ { -+ final String input = String.format(_format.getFilename(), rx, ry); -+ final String filepath = Config.GEODATA_PATH; -+ final File f = new File(filepath + input); -+ if (f.exists() && !f.isDirectory()) -+ { -+ // load geodata -+ if (!loadGeoBlocks(input)) -+ { -+ System.out.println("GeoDataConverter: Unable to load " + input + " region file."); -+ continue; -+ } -+ -+ // recalculate nswe -+ if (!recalculateNswe()) -+ { -+ System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); -+ continue; -+ } -+ -+ // save geodata -+ final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); -+ if (!saveGeoBlocks(output)) -+ { -+ System.out.println("GeoDataConverter: Unable to save " + output + " region file."); -+ continue; -+ } -+ -+ converted++; -+ System.out.println("GeoDataConverter: Created " + output + " region file."); -+ } -+ } -+ } -+ System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); -+ } -+ -+ /** -+ * Loads geo blocks from buffer of the region file. -+ * @param filename : The name of the to load. -+ * @return boolean : True when successful. -+ */ -+ private static boolean loadGeoBlocks(String filename) -+ { -+ // region file is load-able, try to load it -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) -+ { -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) -+ if (_format == GeoFormat.L2OFF) -+ { -+ for (int i = 0; i < 18; i++) -+ { -+ buffer.get(); -+ } -+ } -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ if (_format == GeoFormat.L2J) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2J: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2J: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ else -+ { -+ // get block type -+ final short type = buffer.getShort(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ default: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ if (buffer.remaining() > 0) -+ { -+ System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ return false; -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); -+ return false; -+ } -+ } -+ -+ /** -+ * Recalculate diagonal flags for the region file. -+ * @return boolean : True when successful. -+ */ -+ private static boolean recalculateNswe() -+ { -+ try -+ { -+ for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) -+ { -+ for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) -+ { -+ // get block -+ final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // skip flat blocks -+ if (block instanceof BlockFlat) -+ { -+ continue; -+ } -+ -+ // for complex and multilayer blocks go though all layers -+ short height = Short.MAX_VALUE; -+ int index; -+ while ((index = block.getIndexBelow(x, y, height)) != -1) -+ { -+ // get height and nswe -+ height = block.getHeight(index); -+ byte nswe = block.getNswe(index); -+ -+ // update nswe with diagonal flags -+ nswe = updateNsweBelow(x, y, height, nswe); -+ -+ // set nswe of the cell -+ block.setNswe(index, nswe); -+ } -+ } -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ /** -+ * Updates the NSWE flag with diagonal flags. -+ * @param x : Geodata X coordinate. -+ * @param y : Geodata Y coordinate. -+ * @param z : Geodata Z coordinate. -+ * @param nsweValue : NSWE flag to be updated. -+ * @return byte : Updated NSWE flag. -+ */ -+ private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) -+ { -+ byte nswe = nsweValue; -+ -+ // calculate virtual layer height -+ final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); -+ -+ // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) -+ final byte nsweN = getNsweBelow(x, y - 1, height); -+ final byte nsweS = getNsweBelow(x, y + 1, height); -+ final byte nsweW = getNsweBelow(x - 1, y, height); -+ final byte nsweE = getNsweBelow(x + 1, y, height); -+ -+ // north-west -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NW; -+ } -+ -+ // north-east -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NE; -+ } -+ -+ // south-west -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SW; -+ } -+ -+ // south-east -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SE; -+ } -+ -+ return nswe; -+ } -+ -+ private static byte getNsweBelow(int geoX, int geoY, short worldZ) -+ { -+ // out of geo coordinates -+ if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) -+ { -+ return 0; -+ } -+ -+ // out of geo coordinates -+ if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) -+ { -+ return 0; -+ } -+ -+ // get block -+ final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // get index, when valid, return nswe -+ final int index = block.getIndexBelow(geoX, geoY, worldZ); -+ return index == -1 ? 0 : block.getNswe(index); -+ } -+ -+ /** -+ * Save region file to file. -+ * @param filename : The name of file to save. -+ * @return boolean : True when successful. -+ */ -+ private static boolean saveGeoBlocks(String filename) -+ { -+ try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) -+ { -+ // loop over region blocks and save each block -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[ix][iy].saveBlock(bos); -+ } -+ } -+ -+ // flush data to file -+ bos.flush(); -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ private static void loadGeoengineConfigs() -+ { -+ final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); -+ Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); -+ Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); -+ Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); -+ Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); -+ Config.PATHFINDING = geoData.getBoolean("PathFinding", true); -+ Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -+ Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); -+ Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); -+ Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); -+ Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); -+ Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); -+ } -+} -\ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/geodata/Readme.txt b/L2J_Mobius_5.5_EtinasFate/dist/game/data/geodata/Readme.txt index a480c8c380..2611882e56 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/geodata/Readme.txt +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/geodata/Readme.txt @@ -23,8 +23,8 @@ a - Prerequisites b - Make it work ---------------------------------------------- -To make geodata working: -* unpack your geodata files into "/data/geodata" folder -* open "/config/GeoEngine.ini" with your favorite text editor and then edit following config: - - CoordSynchronize = 2 -* If you do not use any geodata files, the server will automatically change this setting to -1. +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. diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java index 60cfac5c02..63f6ac589c 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java @@ -295,7 +295,7 @@ public class FourSepulchers extends AbstractNpcAI implements IXmlReader { if ((npc != null) && !npc.isDead()) { - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), npc.getSpawn().getLocation().getX() + getRandom(-400, 400), npc.getSpawn().getLocation().getY() + getRandom(-400, 400), npc.getZ(), npc.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), npc.getSpawn().getLocation().getX() + getRandom(-400, 400), npc.getSpawn().getLocation().getY() + getRandom(-400, 400), npc.getZ(), npc.getInstanceWorld()); if (Util.calculateDistance(npc, npc.getSpawn().getLocation(), false, false) < 600) { npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java index 89f40f2ad4..0d925415a5 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java @@ -270,7 +270,7 @@ public class PrimevalIsle extends AbstractNpcAI final double cos = Math.cos(radian); final int newX = (int) (npc.getX() + (cos * distance)); final int newY = (int) (npc.getY() + (sin * distance)); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, npc.getZ(), npc.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, npc.getZ(), npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, loc, 0); } else if (ag_type == 1) diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java index 0a77f7404d..772f5fb5d7 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java @@ -78,7 +78,7 @@ public class BoyAndGirl extends AbstractNpcAI startQuestTimer("NPC_SHOUT", 10000 + (getRandom(5) * 1000), npc, null); npc.setRunning(); final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 200, 600); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); return super.onSpawn(npc); } @@ -86,7 +86,7 @@ public class BoyAndGirl extends AbstractNpcAI public void onMoveFinished(Npc npc) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 200, 600); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); super.onMoveFinished(npc); } diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java index 558f2631e6..3bcf3810cb 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java @@ -48,7 +48,7 @@ public class Devno extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java index 7525cc99e8..ce81f5c05c 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java @@ -48,7 +48,7 @@ public class Eleve extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java index 7d9150b156..f4e942c326 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java @@ -46,7 +46,7 @@ public class Handermonkey extends AbstractNpcAI { final int x = npc.getSpawn().getX() + (getRandom(-100, 100)); final int y = npc.getSpawn().getY() + (getRandom(-100, 100)); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x, y, npc.getZ(), npc.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x, y, npc.getZ(), npc.getInstanceWorld()); addMoveToDesire(npc, loc, 0); } else diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java index c993ce8936..7df61da041 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java @@ -48,7 +48,7 @@ public class Karonf extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java index 6e8cbee920..589d376f6b 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java @@ -48,7 +48,7 @@ public class Marsha extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java index c616abccc6..5c09d531c8 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java @@ -48,7 +48,7 @@ public class Morgan extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java index 3652b4ab02..fe20952121 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java @@ -48,7 +48,7 @@ public class Rubentis extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", 10000 + (getRandom(5) * 1000), npc, null); } diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java index beece64521..b0944f763d 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java @@ -63,7 +63,7 @@ public class Vortex extends AbstractNpcAI final int x = (int) (attackers.getX() + (600 * Math.cos(radians))); final int y = (int) (attackers.getY() + (600 * Math.sin(radians))); final int z = attackers.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(attackers.getX(), attackers.getY(), attackers.getZ(), x, y, z, attackers.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(attackers.getX(), attackers.getY(), attackers.getZ(), x, y, z, attackers.getInstanceWorld()); attackers.broadcastPacket(new FlyToLocation(attackers, x, y, z, FlyToLocation.FlyType.THROW_UP, 800, 800, 800)); attackers.setXYZ(loc); attackers.broadcastPacket(new ValidateLocation(attackers)); diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/others/FleeMonsters.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/others/FleeMonsters.java index 9346aebc95..b5de804b28 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/others/FleeMonsters.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/ai/others/FleeMonsters.java @@ -68,7 +68,7 @@ public class FleeMonsters extends AbstractNpcAI final int posX = (int) (npc.getX() + (FLEE_DISTANCE * Math.cos(radians))); final int posY = (int) (npc.getY() + (FLEE_DISTANCE * Math.sin(radians))); final int posZ = npc.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, attacker.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); return super.onAttack(npc, attacker, damage, isSummon); } diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index 524652cbc1..18511c6541 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { @@ -133,8 +133,8 @@ public class AdminGeodata implements IAdminCommandHandler } case "admin_geomap": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y + " (" + ((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + "," + ((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + " to " + ((((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + "," + ((((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + ")"); break; } diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java index f3dffa1227..76572f7f25 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java @@ -57,8 +57,8 @@ public class AdminMissingHtmls implements IAdminCommandHandler { case "admin_geomap_missing_htmls": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final int topLeftX = (x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE; final int topLeftY = (y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE; final int bottomRightX = (((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1; diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index fdb2319138..a0a34f1051 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -19,9 +19,9 @@ package handlers.admincommandhandlers; import java.util.List; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; +import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.util.BuilderUtil; @@ -44,13 +44,13 @@ public class AdminPathNode implements IAdminCommandHandler } if (activeChar.getTarget() != null) { - final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { BuilderUtil.sendSysMessage(activeChar, "No Route!"); return true; } - for (AbstractNodeLoc a : path) + for (Location a : path) { BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/Blink.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/Blink.java index 22f2c84eae..22697e8e20 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/Blink.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/Blink.java @@ -88,7 +88,7 @@ public class Blink extends AbstractEffect final int y = effected.getY() + y1; final int z = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, destination, _flyType, _flySpeed, _flyDelay, _animationSpeed)); diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/Fear.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/Fear.java index 95a5afdf98..88c795e04b 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/Fear.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/Fear.java @@ -100,7 +100,7 @@ public class Fear extends AbstractEffect final int posY = (int) (effected.getY() + (FEAR_RANGE * Math.sin(radians))); final int posZ = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); } } diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java index fddd94c60a..55c2e78d8a 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java @@ -57,7 +57,7 @@ public class FlyAway extends AbstractEffect final int y = (int) (effector.getY() - (nRadius * (dy / distance))); final int z = effector.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.broadcastPacket(new FlyToLocation(effected, destination, FlyType.THROW_UP)); effected.setXYZ(destination); diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java index 3c3f9aca62..14809ac499 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java @@ -179,7 +179,7 @@ public class KnockBack extends AbstractEffect final int x = (int) (effected.getX() + (_distance * Math.cos(radians))); final int y = (int) (effected.getY() + (_distance * Math.sin(radians))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, loc, _type, _speed, _delay, _animationSpeed)); diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/PullBack.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/PullBack.java index ed6a37eff6..38fe2fe9d0 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/PullBack.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/PullBack.java @@ -73,7 +73,7 @@ public class PullBack extends AbstractEffect } // In retail, you get debuff, but you are not even moved if there is obstacle. You are still disabled from using skills and moving though. - if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effector.getInstanceWorld())) + if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effected.getInstanceWorld())) { effected.broadcastPacket(new FlyToLocation(effected, effector, _type, _speed, _delay, _animationSpeed)); effected.setXYZ(effector.getX(), effector.getY(), GeoEngine.getInstance().getHeight(effector.getX(), effector.getY(), effector.getZ()) + 10); diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java index d6f9664141..813e496c47 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java @@ -87,7 +87,7 @@ public class TeleportToSummon extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = summon.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java index d3c263978c..c9dc34ac22 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java @@ -76,7 +76,7 @@ public class TeleportToTarget extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java index f2148adb54..c7ca6f64cb 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java @@ -65,7 +65,7 @@ public class RollingDice implements IItemHandler final int x = player.getX() + x1; final int y = player.getY() + y1; final int z = player.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); Broadcast.toSelfAndKnownPlayers(player, new Dice(player.getObjectId(), itemId, number, destination.getX(), destination.getY(), destination.getZ())); final SystemMessage sm = new SystemMessage(SystemMessageId.C1_HAS_ROLLED_A_S2); diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/targethandlers/Ground.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/targethandlers/Ground.java index acce8f1b2f..1328ff01b5 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/targethandlers/Ground.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/targethandlers/Ground.java @@ -52,7 +52,7 @@ public class Ground implements ITargetTypeHandler return null; } - if (!GeoEngine.getInstance().canSeeTarget(creature, worldPosition)) + if (!GeoEngine.getInstance().canSeeLocation(creature, worldPosition)) { if (sendMessage) { diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/Config.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/Config.java index 5751a2bf60..99082967f3 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/Config.java @@ -61,6 +61,7 @@ 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; @@ -944,12 +945,17 @@ public class Config // GeoEngine // -------------------------------------------------- public static Path GEODATA_PATH; + public static GeoType GEODATA_TYPE; public static boolean PATHFINDING; public static String PATHFIND_BUFFERS; - public static float LOW_WEIGHT; - public static float MEDIUM_WEIGHT; - public static float HIGH_WEIGHT; - public static float DIAGONAL_WEIGHT; + public static int MOVE_WEIGHT; + public static int MOVE_WEIGHT_DIAG; + public static int OBSTACLE_WEIGHT; + 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; /** Attribute System */ public static int S_WEAPON_STONE; @@ -2500,12 +2506,17 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); + GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); - MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); - HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); - DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); + MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); + OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); + HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); + MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); + MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); // Load AllowedPlayerRaces config file (if exists) final PropertiesParser AllowedPlayerRaces = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE); diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/commons/util/CommonUtil.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/commons/util/CommonUtil.java index 14ae3a4560..ab7837d827 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/commons/util/CommonUtil.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/commons/util/CommonUtil.java @@ -584,4 +584,15 @@ public class CommonUtil final DecimalFormat formatter = new DecimalFormat(format, new DecimalFormatSymbols(Locale.ENGLISH)); return formatter.format(value); } + + /** + * @param numToTest : The number to test. + * @param min : The minimum limit. + * @param max : The maximum limit. + * @return the number or one of the limit (mininum / maximum). + */ + public static int limit(int numToTest, int min, int max) + { + return (numToTest > max) ? max : ((numToTest < min) ? min : numToTest); + } } diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/commons/util/Point2D.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/commons/util/Point2D.java new file mode 100644 index 0000000000..ebb6272e5e --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/commons/util/Point2D.java @@ -0,0 +1,180 @@ +/* + * 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 . + */ +package org.l2jmobius.commons.util; + +import java.util.Objects; + +/** + * A datatype used to retain a 2D (x/y) point. It got the capability to be set and cleaned. + */ +public class Point2D +{ + protected volatile int _x; + protected volatile int _y; + + public Point2D(int x, int y) + { + _x = x; + _y = y; + } + + @Override + public Point2D clone() + { + return new Point2D(_x, _y); + } + + @Override + public String toString() + { + return _x + ", " + _y; + } + + @Override + public int hashCode() + { + return Objects.hash(_x, _y); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final Point2D other = (Point2D) obj; + return (_x == other._x) && (_y == other._y); + } + + /** + * @param x : The X coord to test. + * @param y : The Y coord to test. + * @return True if all coordinates equals this {@link Point2D} coordinates. + */ + public boolean equals(int x, int y) + { + return (_x == x) && (_y == y); + } + + public int getX() + { + return _x; + } + + public void setX(int x) + { + _x = x; + } + + public int getY() + { + return _y; + } + + public void setY(int y) + { + _y = y; + } + + public void set(int x, int y) + { + _x = x; + _y = y; + } + + /** + * Refresh the current {@link Point2D} using a reference {@link Point2D} and a distance. The new destination is calculated to go in opposite side of the {@link Point2D} reference.
+ *
+ * This method is perfect to calculate fleeing characters position. + * @param referenceLoc : The Point2D used as position. + * @param distance : The distance to be set between current and new position. + */ + public void setFleeing(Point2D referenceLoc, int distance) + { + final double xDiff = referenceLoc.getX() - _x; + final double yDiff = referenceLoc.getY() - _y; + + final double yxRation = Math.abs(xDiff / yDiff); + + final int y = (int) (distance / (yxRation + 1)); + final int x = (int) (y * yxRation); + + _x += (xDiff < 0 ? x : -x); + _y += (yDiff < 0 ? y : -y); + } + + public void clean() + { + _x = 0; + _y = 0; + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @return The distance between this {@Point2D} and some given coordinates. + */ + public double distance2D(int x, int y) + { + final double dx = (double) _x - x; + final double dy = (double) _y - y; + + return Math.sqrt((dx * dx) + (dy * dy)); + } + + /** + * @param point : The {@link Point2D} to test. + * @return The distance between this {@Point2D} and the {@link Point2D} set as parameter. + */ + public double distance2D(Point2D point) + { + return distance2D(point.getX(), point.getY()); + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of some given coordinates. + */ + public boolean isIn2DRadius(int x, int y, int radius) + { + return distance2D(x, y) < radius; + } + + /** + * @param point : The Point2D to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of the {@link Point2D} set as parameter. + */ + public boolean isIn2DRadius(Point2D point, int radius) + { + return distance2D(point) < radius; + } +} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/AttackableAI.java index 547963e3e8..dc29d4cf6e 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/AttackableAI.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/ai/AttackableAI.java @@ -578,7 +578,7 @@ public class AttackableAI extends CreatureAI } // Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast) - final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); + final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); moveTo(moveLoc.getX(), moveLoc.getY(), moveLoc.getZ()); } } @@ -801,7 +801,7 @@ public class AttackableAI extends CreatureAI final int newZ = npc.getZ() + 30; // Mobius: Verify destination. Prevents wall collision issues and fixes monsters not avoiding obstacles. - moveTo(GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); + moveTo(GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); } return; } diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/data/SpawnTable.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/data/SpawnTable.java index a81201f69e..19f0db5b3e 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/data/SpawnTable.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/data/SpawnTable.java @@ -114,8 +114,8 @@ public class SpawnTable } // XML file for spawn - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); // Write info to XML @@ -192,8 +192,8 @@ public class SpawnTable if (update) { - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = spawn.getNpcSpawnTemplate() != null ? spawn.getNpcSpawnTemplate().getSpawnTemplate().getFile() : new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); final File tempFile = new File(spawnFile.getAbsolutePath().substring(Config.DATAPACK_ROOT.getAbsolutePath().length() + 1).replace('\\', '/') + ".tmp"); try diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/enums/GeoType.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/enums/GeoType.java new file mode 100644 index 0000000000..f77aa5eac4 --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/enums/GeoType.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.enums; + +public enum GeoType +{ + L2J("%d_%d.l2j"), + L2OFF("%d_%d_conv.dat"); + + private final String _filename; + + private GeoType(String filename) + { + _filename = filename; + } + + public String getFilename() + { + return _filename; + } +} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java new file mode 100644 index 0000000000..e62f50a8df --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java @@ -0,0 +1,144 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 9d255a5f54..7bef9c770b 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,72 +16,63 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.channels.FileChannel.MapMode; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.logging.Level; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; import java.util.logging.Logger; import org.l2jmobius.Config; +import org.l2jmobius.commons.util.CommonUtil; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; -import org.l2jmobius.gameserver.geoengine.geodata.IRegion; -import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; -import org.l2jmobius.gameserver.geoengine.geodata.Region; +import org.l2jmobius.gameserver.enums.GeoType; +import org.l2jmobius.gameserver.enums.MoveDirectionType; +import org.l2jmobius.gameserver.geoengine.geodata.ABlock; +import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; +import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; +import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; +import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.model.interfaces.ILocational; -import org.l2jmobius.gameserver.util.GeoUtils; -import org.l2jmobius.gameserver.util.LinePointIterator; -import org.l2jmobius.gameserver.util.LinePointIterator3D; -/** - * @author -Nemesiss-, HorridoJoho - */ public class GeoEngine { - private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - - private static final int WORLD_MIN_X = -655360; - private static final int WORLD_MIN_Y = -589824; - private static final int WORLD_MIN_Z = -16384; - - /** 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; - - /** The regions array */ - private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; - private static final int ELEVATED_SEE_OVER_DISTANCE = 2; - private static final int MAX_SEE_OVER_HEIGHT = 48; - protected GeoEngine() + private final ABlock[][] _blocks; + private final BlockNull _nullBlock; + + // Pre-allocated buffers. + private final BufferHolder[] _buffers; + + public GeoEngine() { LOGGER.info("GeoEngine: Initializing..."); - for (int i = 0; i < _regions.length(); i++) - { - _regions.set(i, NullRegion.INSTANCE); - } + // Initialize block container. + _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; + + // Load null block. + _nullBlock = new BlockNull(); + + // Initialize multilayer temporarily buffer. + BlockMultilayer.initialize(); + + // Load geo files according to geoengine config setup. int loaded = 0; try { @@ -92,23 +83,52 @@ public class GeoEngine final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); if (Files.exists(geoFilePath)) { - try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + // Region file is load-able, try to load it. + if (loadGeoBlocks(regionX, regionY)) { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); loaded++; } } + else + { + // Region file is not load-able, load null blocks. + loadNullBlocks(regionX, regionY); + } } } } catch (Exception e) { - LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + LOGGER.warning("GeoEngine: Failed to load geodata! " + e); System.exit(1); } - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + // Release multilayer block temporarily buffer. + BlockMultilayer.release(); + + String[] array = Config.PATHFIND_BUFFERS.split(";"); + _buffers = new BufferHolder[array.length]; + + int count = 0; + for (int i = 0; i < array.length; i++) + { + String buf = array[i]; + String[] args = buf.split("x"); + + try + { + int size = Integer.parseInt(args[1]); + count += size; + _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); + } + catch (Exception e) + { + LOGGER.warning("Could not load buffer setting:" + buf + ". " + e); + } + } + LOGGER.info("Loaded " + count + " node buffers."); + // Avoid wrong configs when no files are loaded. if ((loaded == 0) && Config.PATHFINDING) { @@ -118,616 +138,913 @@ public class GeoEngine } /** - * @param geoX - * @param geoY - * @return the region + * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer and log this situation. + * @param size : pre-calculated minimal required size + * @return NodeBuffer : buffer */ - private IRegion getRegion(int geoX, int geoY) + private NodeBuffer getBuffer(int size) { - final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); - if ((region < 0) || (region >= _regions.length())) + NodeBuffer current = null; + for (BufferHolder holder : _buffers) { - return null; - } - return _regions.get(region); - } - - /** - * @param filePath - * @param regionX - * @param regionY - * @throws IOException - */ - 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()).load().order(ByteOrder.LITTLE_ENDIAN))); - } - } - - /** - * @param regionX - * @param regionY - */ - public void unloadRegion(int regionX, int regionY) - { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); - } - - /** - * @param geoX - * @param geoY - * @return if geodata exist - */ - public boolean hasGeoPos(int geoX, int geoY) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return false; - } - return region.hasGeo(); - } - - /** - * Checks the specified position for available geodata. - * @param x the world x - * @param y the world y - * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise - */ - public boolean hasGeo(int x, int y) - { - return hasGeoPos(getGeoX(x), getGeoY(y)); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe check - */ - public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return true; - } - return region.checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe anti-corner cut - */ - public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) - { - boolean can = true; - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - return can && checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the nearest Z value - */ - public int getNearestZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNearestZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next lower Z value - */ - public int getNextLowerZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextLowerZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next higher Z value - */ - public int getNextHigherZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextHigherZ(geoX, geoY, worldZ); - } - - /** - * Gets the Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHeight(int x, int y, int z) - { - return getNearestZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next lower Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getLowerHeight(int x, int y, int z) - { - return getNextLowerZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next higher Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHigherHeight(int x, int y, int z) - { - return getNextHigherZ(getGeoX(x), getGeoY(y), z); - } - - /** - * @param worldX - * @return the geo X - */ - public int getGeoX(int worldX) - { - return (worldX - WORLD_MIN_X) / 16; - } - - /** - * @param worldY - * @return the geo Y - */ - public int getGeoY(int worldY) - { - return (worldY - WORLD_MIN_Y) / 16; - } - - /** - * @param worldZ - * @return the geo Z - */ - public int getGeoZ(int worldZ) - { - return (worldZ - WORLD_MIN_Z) / 16; - } - - /** - * @param geoX - * @return the world X - */ - public int getWorldX(int geoX) - { - return (geoX * 16) + WORLD_MIN_X + 8; - } - - /** - * @param geoY - * @return the world Y - */ - public int getWorldY(int geoY) - { - return (geoY * 16) + WORLD_MIN_Y + 8; - } - - /** - * @param geoZ - * @return the world Z - */ - public int getWorldZ(int geoZ) - { - return (geoZ * 16) + WORLD_MIN_Z + 8; - } - - /** - * Can see target. Doors as target always return true. Checks doors between. - * @param cha the character - * @param target the target - * @return {@code true} if the character can see the target (LOS), {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, WorldObject target) - { - if (target.isDoor()) - { - // Can always see doors. - return true; - } - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); - } - - /** - * Can see target. Checks doors between. - * @param cha the character - * @param worldPosition the world position - * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) - { - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param world - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @param tworld the target's instanceId - * @return - */ - public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) - { - return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instance - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) - { - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) - { - return false; - } - return canSeeTarget(x, y, z, tx, ty, tz); - } - - /** - * @param prevX - * @param prevY - * @param prevGeoZ - * @param curX - * @param curY - * @param nswe - * @return the LOS Z value - */ - private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) - { - if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) - { - throw new RuntimeException("Multiple directions!"); - } - - if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) - { - return getNearestZ(curX, curY, prevGeoZ); - } - - return getNextHigherZ(curX, curY, prevGeoZ); - } - - /** - * Can see target. Does not check doors between. - * @param xValue the x coordinate - * @param yValue the y coordinate - * @param zValue the z coordinate - * @param txValue the target's x coordinate - * @param tyValue the target's y coordinate - * @param tzValue the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) - { - int x = xValue; - int y = yValue; - int tx = txValue; - int ty = tyValue; - - int geoX = getGeoX(x); - int geoY = getGeoY(y); - int tGeoX = getGeoX(tx); - int tGeoY = getGeoY(ty); - - int z = getNearestZ(geoX, geoY, zValue); - int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - // Fastpath. - if ((geoX == tGeoX) && (geoY == tGeoY)) - { - if (hasGeoPos(tGeoX, tGeoY)) - { - return z == tz; - } - return true; - } - - if (tz > z) - { - int tmp = tx; - tx = x; - x = tmp; - - tmp = ty; - ty = y; - y = tmp; - - tmp = tz; - tz = z; - z = tmp; - - tmp = tGeoX; - tGeoX = geoX; - geoX = tmp; - - tmp = tGeoY; - tGeoY = geoY; - geoY = tmp; - } - - final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); - // First point is guaranteed to be available, skip it, we can always see our own position. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - final int prevZ = pointIter.z(); - int prevGeoZ = prevZ; - int ptIndex = 0; - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - - if ((curX == prevX) && (curY == prevY)) + // Find proper size of buffer. + if (holder._size < size) { continue; } - final int beeCurZ = pointIter.z(); - int curGeoZ = prevGeoZ; - - // Check if the position has geodata. - if (hasGeoPos(curX, curY)) + // Find unlocked NodeBuffer. + for (NodeBuffer buffer : holder._buffer) { - final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); - curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); - int maxHeight; - if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) + if (!buffer.isLocked()) { - maxHeight = z + MAX_SEE_OVER_HEIGHT; - } - else - { - maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; + continue; } - boolean canSeeThrough = false; - if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) + return buffer; + } + + LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); + + // NodeBuffer not found, allocate temporary buffer. + current = new NodeBuffer(holder._size); + current.isLocked(); + } + + return current; + } + + /** + * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + * @return boolean : True, when geodata file was loaded without problem. + */ + private boolean loadGeoBlocks(int regionX, int regionY) + { + final String filename = String.format(Config.GEODATA_TYPE.getFilename(), regionX, regionY); + final String filepath = Config.GEODATA_PATH + "\\" + filename; + + // Standard load. + try (RandomAccessFile raf = new RandomAccessFile(filepath, "r"); + FileChannel fc = raf.getChannel()) + { + // Initialize file buffer. + MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // Load 18B header for L2OFF geodata (1st and 2nd byte...region X and Y). + if (Config.GEODATA_TYPE == GeoType.L2OFF) + { + for (int i = 0; i < 18; i++) { - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) + buffer.get(); + } + } + + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Loop over region blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + if (Config.GEODATA_TYPE == GeoType.L2J) { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) - { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); + // Get block type. + final byte type = buffer.get(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + case GeoStructure.TYPE_MULTILAYER_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + default: + { + throw new IllegalArgumentException("Unknown block type: " + type); + } + } } else { - canSeeThrough = true; + // Get block type. + final short type = buffer.getShort(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + default: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + } } } + } + + // Check data consistency. + if (buffer.remaining() > 0) + { + LOGGER.warning("Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); + } + + // Loading was successful. + return true; + } + catch (Exception e) + { + // An error occured while loading, load null blocks. + LOGGER.warning("Error loading " + filename + " region file. " + e); + + // Replace whole region file with null blocks. + loadNullBlocks(regionX, regionY); + + // Loading was not successful. + return false; + } + } + + /** + * Loads null blocks. Used when no region file is detected or an error occurs during loading. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + */ + private void loadNullBlocks(int regionX, int regionY) + { + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Load all null blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + _blocks[blockX + ix][blockY + iy] = _nullBlock; + } + } + } + + /** + * Converts world X to geodata X. + * @param worldX + * @return int : Geo X + */ + public static int getGeoX(int worldX) + { + return (worldX - World.WORLD_X_MIN) >> 4; + } + + /** + * Converts world Y to geodata Y. + * @param worldY + * @return int : Geo Y + */ + public static int getGeoY(int worldY) + { + return (worldY - World.WORLD_Y_MIN) >> 4; + } + + /** + * Converts geodata X to world X. + * @param geoX + * @return int : World X + */ + public static int getWorldX(int geoX) + { + return (geoX << 4) + World.WORLD_X_MIN + 8; + } + + /** + * Converts geodata Y to world Y. + * @param geoY + * @return int : World Y + */ + public static int getWorldY(int geoY) + { + return (geoY << 4) + World.WORLD_Y_MIN + 8; + } + + /** + * Returns block of geodata on given coordinates. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return {@link ABlock} : Block of geodata. + */ + public ABlock getBlock(int geoX, int geoY) + { + final int x = geoX / GeoStructure.BLOCK_CELLS_X; + if ((x < 0) || (x >= GeoStructure.GEO_BLOCKS_X)) + { + return null; + } + final int y = geoY / GeoStructure.BLOCK_CELLS_Y; + if ((y < 0) || (y >= GeoStructure.GEO_BLOCKS_Y)) + { + return null; + } + return _blocks[x][y]; + } + + /** + * Check if geo coordinates has geo. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return boolean : True, if given geo coordinates have geodata + */ + public boolean hasGeoPos(int geoX, int geoY) + { + final ABlock block = getBlock(geoX, geoY); + return (block != null) && block.hasGeoPos(); + } + + /** + * Returns the height of cell, which is closest to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell geodata Z coordinate, closest to given coordinates. + */ + public short getHeightNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return (short) worldZ; + } + return block.getHeightNearest(geoX, geoY, worldZ); + } + + /** + * Returns the NSWE flag byte of cell, which is closes to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. + */ + public byte getNsweNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return GeoStructure.CELL_FLAG_ALL; + } + return block.getNsweNearest(geoX, geoY, worldZ); + } + + /** + * Check if world coordinates has geo. + * @param worldX : World X + * @param worldY : World Y + * @return boolean : True, if given world coordinates have geodata + */ + public boolean hasGeo(int worldX, int worldY) + { + return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param loc : The location used as reference. + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(Location loc) + { + return getHeightNearest(getGeoX(loc.getX()), getGeoY(loc.getY()), loc.getZ()); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param worldX : world x + * @param worldY : world y + * @param worldZ : world z + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(int worldX, int worldY, int worldZ) + { + return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); + } + + /** + * Check line of sight from {@link WorldObject} to {@link WorldObject}.
+ * @param object : The origin object. + * @param target : The target object. + * @return True, when object can see target. + */ + public boolean canSeeTarget(WorldObject object, WorldObject target) + { + // Can always see doors. + if (target.isDoor()) + { + return true; + } + + if (object.getInstanceWorld() != target.getInstanceWorld()) + { + return false; + } + + if (DoorData.getInstance().checkIfDoorsBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld())) + { + return false; + } + + // Get object's and target's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + double theight = 0; + if (target instanceof Creature) + { + theight += (((Creature) target).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + return canSee(object.getX(), object.getY(), object.getZ(), oheight, target.getX(), target.getY(), target.getZ(), theight) && canSee(target.getX(), target.getY(), target.getZ(), theight, object.getX(), object.getY(), object.getZ(), oheight); + } + + /** + * Check line of sight from {@link WorldObject} to {@link Location}.
+ * Note: The check uses {@link Location}'s real Z coordinate (e.g. point above ground), not its geodata representation. + * @param object : The origin object. + * @param position : The target position. + * @return True, when object can see position. + */ + public boolean canSeeLocation(WorldObject object, Location position) + { + // Get object and location coordinates. + int ox = object.getX(); + int oy = object.getY(); + int oz = object.getZ(); + int tx = position.getX(); + int ty = position.getY(); + int tz = position.getZ(); + + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld())) + { + return false; + } + + // Get object's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + // Perform geodata check. + return canSee(ox, oy, oz, oheight, tx, ty, tz, 0) && canSee(tx, ty, tz, 0, ox, oy, oz, oheight); + } + + /** + * Simple check for origin to target visibility.
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param oheight : The height of origin, used as start point. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param theight : The height of target, used as end point. + * @return True, when origin can see target. + */ + public boolean canSee(int ox, int oy, int oz, double oheight, int tx, int ty, int tz, double theight) + { + // Get origin geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get target geodata coordinates. + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check being on same cell and layer (index). + // Note: Get index must use origin height increased by cell height, the method returns index to height exclusive self. + int index = block.getIndexBelow(gox, goy, oz + GeoStructure.CELL_HEIGHT); + if (index < 0) + { + return false; + } + + if ((gox == gtx) && (goy == gty)) + { + return index == block.getIndexBelow(gtx, gty, tz + GeoStructure.CELL_HEIGHT); + } + + // Get ground and nswe flag. + int groundZ = block.getHeight(index); + int nswe = block.getNswe(index); + + // Get delta coordinates, slope of line (XY, XZ) and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double dz = (tz + theight) - (oz + oheight); + final double m = (double) dy / dx; + final double mz = dz / Math.sqrt((dx * dx) + (dy * dy)); + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); + + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) + { + // Set next cell in X direction. + gridX += mdt.getStepX(); + gox += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); - if (!canSeeThrough) - { - return false; - } + // Set next cell in Y direction. + gridY += mdt.getStepY(); + goy += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevGeoZ = curGeoZ; - ++ptIndex; + // Get block of the next cell. + block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get line of sight height (including Z slope). + double losz = oz + oheight + Config.MAX_OBSTACLE_HEIGHT; + losz += mz * Math.sqrt(((checkX - ox) * (checkX - ox)) + ((checkY - oy) * (checkY - oy))); + + // Check line of sight going though wall (vertical check). + + // Get index of particular layer, based on last iterated cell conditions. + boolean canMove = (nswe & dir) != 0; + if (canMove) + { + // No wall present, get next cell below current cell. + index = block.getIndexBelow(gox, goy, groundZ + GeoStructure.CELL_IGNORE_HEIGHT); + } + else + { + // Wall present, get next cell above current cell. + index = block.getIndexAbove(gox, goy, groundZ - (2 * GeoStructure.CELL_HEIGHT)); + } + + // Next cell's does not exist (no geodata with valid condition), return fail. + if (index < 0) + { + return false; + } + + // Get next cell's layer height. + int z = block.getHeight(index); + + // Perform sine of sight check (next cell is above line of sight line), return fail. + if (z > losz) + { + return false; + } + + // Next cell is accessible, update z and NSWE. + groundZ = z; + nswe = block.getNswe(index); } + // Iteration is completed, no obstacle is found. return true; } /** - * Move check. - * @param x the x coordinate - * @param y the y coordinate - * @param zValue the z coordinate - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tzValue the target's z coordinate - * @param instance the instance - * @return the last Location (x,y,z) where player can walk - just before wall + * Check movement from coordinates to coordinates.
+ * Note: The Z coordinates are supposed to be already validated geodata coordinates. + * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return True, when target coordinates are reachable from origin coordinates. */ - public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(x); - final int geoY = getGeoY(y); - final int z = getNearestZ(geoX, geoY, zValue); - final int tGeoX = getGeoX(tx); - final int tGeoY = getGeoY(ty); - final int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return new Location(x, y, getHeight(x, y, z)); - } - if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) - { - return new Location(x, y, getHeight(x, y, z)); + return false; } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = z; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return false; + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + int goz = getHeightNearest(gox, goy, oz); + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check movement within same cell. + if ((gox == gtx) && (goy == gty)) + { + return goz == getHeight(tx, ty, tz); + } + + // Get nswe flag. + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - // Can't move, return previous location. - return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check point heading into obstacle, if so return current point. + if ((nswe & dir) == 0) + { + return false; + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return false; + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != tz)) - { - // Different floors, return start location. - return new Location(x, y, z); - } - - return new Location(tx, ty, tz); + // When origin Z is target Z, the move is successful. + return goz == getHeight(tx, ty, tz); } /** - * Checks if its possible to move from one location to another. - * @param fromX the X coordinate to start checking from - * @param fromY the Y coordinate to start checking from - * @param fromZvalue the Z coordinate to start checking from - * @param toX the X coordinate to end checking at - * @param toY the Y coordinate to end checking at - * @param toZvalue the Z coordinate to end checking at - * @param instance the instance - * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + * Check movement from origin to target coordinates. Returns last available point in the checked path.
+ * Target X and Y reachable and Z is on same floor: + *
    + *
  • Location of the target with corrected Z value from geodata.
  • + *
+ * Target X and Y reachable but Z is on another floor: + *
    + *
  • Location of the origin with corrected Z value from geodata.
  • + *
+ * Target X and Y not reachable: + *
    + *
  • Last accessible location in destination to target.
  • + *
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return The {@link Location} representing last point of movement (e.g. just before wall). */ - public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + public Location getValidLocation(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(fromX); - final int geoY = getGeoY(fromY); - final int fromZ = getNearestZ(geoX, geoY, fromZvalue); - final int tGeoX = getGeoX(toX); - final int tGeoY = getGeoY(toY); - final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); - - if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) - { - return false; + return new Location(ox, oy, oz); } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = fromZ; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return new Location(ox, oy, oz); + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + final int gtz = getHeightNearest(gtx, gty, tz); + int goz = getHeightNearest(gox, goy, oz); + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - return false; - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check target cell is out of geodata grid (world coordinates). + if ((nx < 0) || (nx >= GeoStructure.GEO_CELLS_X) || (ny < 0) || (ny >= GeoStructure.GEO_CELLS_Y)) + { + return new Location(checkX, checkY, goz); + } + + // Check point heading into obstacle, if so return current (border) point. + if ((nswe & dir) == 0) + { + return new Location(checkX, checkY, goz); + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current (border) point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return new Location(checkX, checkY, goz); + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) - { - // Different floors. - return false; - } - - return true; + // Compare Z coordinates: + // If same, path is okay, return target point and fix its Z geodata coordinate. + // If not same, path is does not exist, return origin point. + return goz == gtz ? new Location(tx, ty, gtz) : new Location(ox, oy, oz); } + /** + * Returns the list of location objects as a result of complete path calculation. + * @param ox : origin x + * @param oy : origin y + * @param oz : origin z + * @param tx : target x + * @param ty : target y + * @param tz : target z + * @param instance + * @return {@code List} : complete path from nodes + */ + public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + { + // Get origin and check existing geo coords. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + if (!hasGeoPos(gox, goy)) + { + return Collections.emptyList(); + } + + int goz = getHeightNearest(gox, goy, oz); + + // Get target and check existing geo coords. + int gtx = getGeoX(tx); + int gty = getGeoY(ty); + if (!hasGeoPos(gtx, gty)) + { + return Collections.emptyList(); + } + + int gtz = getHeightNearest(gtx, gty, tz); + + // Prepare buffer for pathfinding calculations. + NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + if (buffer == null) + { + return Collections.emptyList(); + } + + // Find path. + List path = null; + try + { + path = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + if (path.isEmpty()) + { + return Collections.emptyList(); + } + } + catch (Exception e) + { + return Collections.emptyList(); + } + finally + { + buffer.free(); + } + + // Check path. + if (path.size() < 3) + { + return path; + } + + // Get path list iterator. + ListIterator point = path.listIterator(); + + // Get node A (origin). + int nodeAx = ox; + int nodeAy = oy; + int nodeAz = goz; + + // Get node B. + Location nodeB = point.next(); + + // Iterate thought the path to optimize it. + while (point.hasNext()) + { + // Get node C. + Location nodeC = path.get(point.nextIndex()); + + // Check movement from node A to node C. + if (canMoveToTarget(nodeAx, nodeAy, nodeAz, nodeC.getX(), nodeC.getY(), nodeC.getZ(), instance)) + { + // Can move from node A to node C. + // Remove node B. + point.remove(); + } + else + { + // Can not move from node A to node C. + // Set node A (node B is part of path, update A coordinates). + nodeAx = nodeB.getX(); + nodeAy = nodeB.getY(); + nodeAz = nodeB.getZ(); + } + + // Set node B. + nodeB = point.next(); + } + + return path; + } + + /** + * NodeBuffer container with specified size and count of separate buffers. + */ + private static class BufferHolder + { + final int _size; + final List _buffer; + + public BufferHolder(int size, int count) + { + _size = size; + _buffer = new ArrayList<>(count); + + for (int i = 0; i < count; i++) + { + _buffer.add(new NodeBuffer(size)); + } + } + } + + /** + * Returns the instance of the {@link GeoEngine}. + * @return {@link GeoEngine} : The instance. + */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -737,4 +1054,4 @@ public class GeoEngine { protected static final GeoEngine INSTANCE = new GeoEngine(); } -} +} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java deleted file mode 100644 index cc76b7523a..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ /dev/null @@ -1,301 +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 . - */ -package org.l2jmobius.gameserver.geoengine; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; -import org.l2jmobius.gameserver.model.World; -import org.l2jmobius.gameserver.model.instancezone.Instance; - -/** - * @author -Nemesiss- - */ -public class GeoEnginePathfinding -{ - private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); - - private BufferInfo[] _buffers; - - protected GeoEnginePathfinding() - { - try - { - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - - _buffers = 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); - } - - _buffers[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"); - } - } - - public boolean pathNodesExist(short regionoffset) - { - return false; - } - - public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) - { - 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)))); - if (buffer == null) - { - return null; - } - - List path = null; - try - { - final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); - - if (result == null) - { - return null; - } - - path = constructPath(result); - } - catch (Exception e) - { - LOGGER.warning(e.getMessage()); - return null; - } - finally - { - buffer.free(); - } - - // check path - if (path.size() < 3) - { - return path; - } - - int currentX, currentY, currentZ; - ListIterator middlePoint; - - middlePoint = path.listIterator(); - currentX = x; - currentY = y; - currentZ = z; - - while (middlePoint.hasNext()) - { - final AbstractNodeLoc locMiddle = middlePoint.next(); - if (!middlePoint.hasNext()) - { - break; - } - - final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); - if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) - { - middlePoint.remove(); - } - else - { - currentX = locMiddle.getX(); - currentY = locMiddle.getY(); - currentZ = locMiddle.getZ(); - } - } - - return path; - } - - /** - * Convert geodata position to pathnode position - * @param geo_pos - * @return pathnode position - */ - public short getNodePos(int geo_pos) - { - return (short) (geo_pos >> 3); // OK? - } - - /** - * Convert node position to pathnode block position - * @param node_pos - * @return pathnode block position (0...255) - */ - public short getNodeBlock(int node_pos) - { - return (short) (node_pos % 256); - } - - public byte getRegionX(int node_pos) - { - return (byte) ((node_pos >> 8) + World.TILE_X_MIN); - } - - public byte getRegionY(int node_pos) - { - return (byte) ((node_pos >> 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 node_x rx - * @return - */ - public int calculateWorldX(short node_x) - { - return World.MAP_MIN_X + (node_x * 128) + 48; - } - - /** - * Convert pathnode y to World y position - * @param node_y - * @return - */ - public int calculateWorldY(short node_y) - { - return World.MAP_MIN_Y + (node_y * 128) + 48; - } - - private List constructPath(AbstractNode nodeValue) - { - final LinkedList path = new LinkedList<>(); - int previousDirectionX = Integer.MIN_VALUE; - int previousDirectionY = Integer.MIN_VALUE; - int directionX, directionY; - - AbstractNode node = nodeValue; - while (node.getParent() != null) - { - directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); - directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); - - // only add a new route point if moving direction changes - if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) - { - previousDirectionX = directionX; - previousDirectionY = directionY; - - path.addFirst(node.getLoc()); - node.setLoc(null); - } - - node = node.getParent(); - } - - return path; - } - - private CellNodeBuffer alloc(int size) - { - CellNodeBuffer current = null; - for (BufferInfo i : _buffers) - { - if (i.mapSize >= size) - { - for (CellNodeBuffer buf : i.buffer) - { - if (buf.lock()) - { - current = buf; - break; - } - } - if (current != null) - { - break; - } - - // not found, allocate temporary buffer - current = new CellNodeBuffer(i.mapSize); - current.lock(); - if (i.buffer.size() < i.count) - { - i.buffer.add(current); - break; - } - } - } - - return current; - } - - private static final class BufferInfo - { - final int mapSize; - final int count; - ArrayList buffer; - - public BufferInfo(int size, int cnt) - { - mapSize = size; - count = cnt; - buffer = new ArrayList<>(count); - } - } - - public static GeoEnginePathfinding getInstance() - { - return SingletonHolder.INSTANCE; - } - - private static class SingletonHolder - { - protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); - } -} diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java new file mode 100644 index 0000000000..49ec35aa1c --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java @@ -0,0 +1,85 @@ +/* + * 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 . + */ +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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @param index : Index of the cell. + * @return short : Cell geodata Z coordinate, below given coordinates. + */ + public abstract byte getNswe(int index); +} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java new file mode 100644 index 0000000000..f5997bf287 --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java @@ -0,0 +1,130 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java new file mode 100644 index 0000000000..9af9d2a28a --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java @@ -0,0 +1,95 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java new file mode 100644 index 0000000000..31fbf5d477 --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java new file mode 100644 index 0000000000..f77d21d84b --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java @@ -0,0 +1,68 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +public class BlockNull extends ABlock +{ + @Override + public boolean hasGeoPos() + { + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java deleted file mode 100644 index 61ea4fcf82..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java +++ /dev/null @@ -1,48 +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 . - */ -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() - { - } -} diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java deleted file mode 100644 index 3c8de1a7f7..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java +++ /dev/null @@ -1,77 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java deleted file mode 100644 index 1ccdefe3da..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java +++ /dev/null @@ -1,56 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java new file mode 100644 index 0000000000..691b164e0c --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ +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_ALL = 0x0F; + + // 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; +} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java deleted file mode 100644 index 25eafc5a04..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java +++ /dev/null @@ -1,42 +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 . - */ -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); -} diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java deleted file mode 100644 index 0d2c765002..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java +++ /dev/null @@ -1,47 +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 . - */ -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(); -} diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java deleted file mode 100644 index 52df457360..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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); - - // 1 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; - } -} diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java deleted file mode 100644 index 72a5a635c7..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java +++ /dev/null @@ -1,55 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author HorridoJoho - */ -public final class NullRegion implements IRegion -{ - public static final NullRegion INSTANCE = new NullRegion(); - - @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 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; - } -} diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java deleted file mode 100644 index fc924cb875..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java +++ /dev/null @@ -1,92 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java deleted file mode 100644 index 5087513ed9..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java +++ /dev/null @@ -1,87 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public abstract class AbstractNode -{ - private T _loc; - private AbstractNode _parent; - - public AbstractNode(T loc) - { - _loc = loc; - } - - public void setParent(AbstractNode p) - { - _parent = p; - } - - public AbstractNode getParent() - { - return _parent; - } - - public T getLoc() - { - return _loc; - } - - public void setLoc(T l) - { - _loc = l; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); - return result; - } - - @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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java deleted file mode 100644 index 7223b5b2be..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java +++ /dev/null @@ -1,33 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -/** - * @author -Nemesiss- - */ -public abstract class AbstractNodeLoc -{ - public abstract int getX(); - - public abstract int getY(); - - public abstract int getZ(); - - public abstract int getNodeX(); - - public abstract int getNodeY(); -} diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java deleted file mode 100644 index 3425234e0e..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public class CellNode extends AbstractNode -{ - 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java deleted file mode 100644 index 522610bb7c..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java +++ /dev/null @@ -1,348 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -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 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) - { - _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(); - } - - public final List debugPath() - { - final List 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 void getNeighbors() - { - if (!_current.getLoc().canGoAll()) - { - 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); - } - - // SouthEast - if ((nodeE != null) && (nodeS != null)) - { - if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) - { - addNode(x + 1, y + 1, z, true); - } - } - - // SouthWest - if ((nodeS != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) - { - addNode(x - 1, y + 1, z, true); - } - } - - // NorthEast - if ((nodeN != null) && (nodeE != null)) - { - if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) - { - addNode(x + 1, y - 1, z, true); - } - } - - // NorthWest - if ((nodeN != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) - { - addNode(x - 1, y - 1, z, true); - } - } - } - - private 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(); - // reinit node if needed - if (result.getLoc() != null) - { - result.getLoc().set(x, y, z); - } - else - { - result.setLoc(new NodeLoc(x, y, z)); - } - } - - return result; - } - - private 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 boolean isHighWeight(int x, int y, int z) - { - final CellNode result = getNode(x, y, z); - if (result == null) - { - return true; - } - - if (!result.getLoc().canGoAll()) - { - return true; - } - if (Math.abs(result.getLoc().getZ() - z) > 16) - { - return true; - } - - return false; - } - - private 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java new file mode 100644 index 0000000000..fa5ca43c2f --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -0,0 +1,111 @@ +/* + * 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 . + */ +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 geodata values. + private int _geoX; + private int _geoY; + private byte _nswe; + + // 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; + + _costG = 0; + _costH = 0; + _costF = 0; + + _parent = null; + } + + public void setGeo(int gx, int gy, int gz, byte nswe) + { + super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); + + _geoX = gx; + _geoY = gy; + _nswe = nswe; + } + + 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 int getCostF() + { + return _costF; + } + + public Node getParent() + { + return _parent; + } + + @Override + public int compareTo(Node o) + { + return _costF - o._costF; + } +} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java new file mode 100644 index 0000000000..b464212c96 --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -0,0 +1,368 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.awt.Color; +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; +import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; + +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 _opened; + // Container holding Nodes already explored. + private final List _closed; + + // Target coordinates. + private int _gtx; + private int _gty; + private int _gtz; + + // Pathfinding statistics. + private long _timeStamp; + private long _lastElapsedTime; + + 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 findPath(int gox, int goy, int goz, int gtx, int gty, int gtz) + { + // Set start timestamp. + _timeStamp = System.currentTimeMillis(); + + // 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)); + _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 constructPath() + { + // Create result. + final LinkedList 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; + } + + /** + * Creates list of Nodes to show debug path. + * @param debug : The debug packet to add debug informations in. + */ + public void debugPath(ExServerPrimitive debug) + { + // Add all opened node as yellow points. + for (Node n : _opened) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); + } + + // Add all opened node as blue points. + for (Node n : _closed) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); + } + } + + 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; + + _lastElapsedTime = System.currentTimeMillis() - _timeStamp; + _lock.unlock(); + } + + public long getElapsedTime() + { + return _lastElapsedTime; + } + + /** + * Expand the current {@link Node} by exploring its neighbors (axially and diagonally). + */ + private void expand() + { + // Movement is blocked, skip. + final byte nswe = _current.getNSWE(); + if (nswe == 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; + + // Can move north, expand. + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + } + + // Can move south, expand. + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + } + + // Can move west, expand. + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move east, expand. + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move north-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move north-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + } + + /** + * 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 weight : The weight of movement to the new node. + * @return The nswe of the added node. Blank, if not added. + */ + private byte addNode(int gx, int gy, int gzValue, int weight) + { + // 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]; + + // Set node geodata coordinates. + node.setGeo(gx, gy, gz, nswe); + + // 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, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + _opened.add(node); + _bufferIndex++; + return nswe; + } + + /** + * Calculate cost H value, calculated using diagonal distance method.
+ * 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. + final int dx = Math.abs(gx - _gtx); + final int dy = Math.abs(gy - _gty); + final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + + // Get diagonal and axial differences to the target. + final int dd = Math.min(dx, dy); + final int da = Math.max(dx, dy) - dd; + + // Calculate the diagonal distance of the node to the target. + return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + } +} \ No newline at end of file diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java deleted file mode 100644 index e3bad04b25..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; - -/** - * @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 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 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; - // return super.hashCode(); - } - - @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; - if (_x != other._x) - { - return false; - } - if (_y != other._y) - { - return false; - } - if (_goNorth != other._goNorth) - { - return false; - } - if (_goEast != other._goEast) - { - return false; - } - if (_goSouth != other._goSouth) - { - return false; - } - if (_goWest != other._goWest) - { - return false; - } - if (_geoHeight != other._geoHeight) - { - return false; - } - return true; - } -} diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java index 1faf1ae423..ed261765e1 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java @@ -93,15 +93,15 @@ public class ZoneManager implements IXmlReader private static final Map SETTINGS = new HashMap<>(); private static final int SHIFT_BY = 15; - private static final int OFFSET_X = Math.abs(World.MAP_MIN_X >> SHIFT_BY); - private static final int OFFSET_Y = Math.abs(World.MAP_MIN_Y >> SHIFT_BY); + private static final int OFFSET_X = Math.abs(World.WORLD_X_MIN >> SHIFT_BY); + private static final int OFFSET_Y = Math.abs(World.WORLD_Y_MIN >> SHIFT_BY); private final Map, ConcurrentHashMap> _classZones = new ConcurrentHashMap<>(); private final Map _spawnTerritories = new ConcurrentHashMap<>(); private final AtomicInteger _lastDynamicId = new AtomicInteger(300000); private List _debugItems; - private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.MAP_MAX_X >> SHIFT_BY) + OFFSET_X + 1][(World.MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y + 1]; + private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.WORLD_X_MAX >> SHIFT_BY) + OFFSET_X + 1][(World.WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y + 1]; /** * Instantiates a new zone manager. diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/Location.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/Location.java index 73689ab1a6..ca3b22a3f0 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/Location.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/Location.java @@ -16,30 +16,30 @@ */ package org.l2jmobius.gameserver.model; +import java.util.Objects; + +import org.l2jmobius.commons.util.Point2D; import org.l2jmobius.gameserver.model.interfaces.ILocational; import org.l2jmobius.gameserver.model.interfaces.IPositionable; /** - * Location data transfer object.
- * Contains coordinates data, heading and instance Id. - * @author Zoey76 + * A datatype used to retain a 3D (x/y/z/heading) point. It got the capability to be set and cleaned. */ -public class Location implements IPositionable +public class Location extends Point2D implements IPositionable { - protected int _x; - protected int _y; - protected int _z; - private int _heading; + protected volatile int _z; + protected volatile int _heading; public Location(int x, int y, int z) { - this(x, y, z, 0); + super(x, y); + _z = z; + _heading = 0; } public Location(int x, int y, int z, int heading) { - _x = x; - _y = y; + super(x, y); _z = z; _heading = heading; } @@ -51,9 +51,8 @@ public class Location implements IPositionable public Location(StatSet set) { - _x = set.getInt("x"); - _y = set.getInt("y"); - _z = set.getInt("z"); + super(set.getInt("x", 0), set.getInt("y", 0)); + _z = set.getInt("z", 0); _heading = set.getInt("heading", 0); } @@ -146,6 +145,25 @@ public class Location implements IPositionable _heading = loc.getHeading(); } + @Override + public void clean() + { + super.clean(); + _z = 0; + } + + @Override + public Location clone() + { + return new Location(_x, _y, _z); + } + + @Override + public int hashCode() + { + return (31 * super.hashCode()) + Objects.hash(_z); + } + @Override public boolean equals(Object obj) { diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/World.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/World.java index c1510e8c0e..0d796dd834 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/World.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/World.java @@ -66,19 +66,19 @@ public class World public static final int TILE_Y_MAX = 26; public static final int TILE_ZERO_COORD_X = 20; public static final int TILE_ZERO_COORD_Y = 18; - public static final int MAP_MIN_X = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; - public static final int MAP_MIN_Y = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; + public static final int WORLD_X_MIN = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; + public static final int WORLD_Y_MIN = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; - public static final int MAP_MAX_X = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; - public static final int MAP_MAX_Y = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; + public static final int WORLD_X_MAX = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; + public static final int WORLD_Y_MAX = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; /** Calculated offset used so top left region is 0,0 */ - public static final int OFFSET_X = Math.abs(MAP_MIN_X >> SHIFT_BY); - public static final int OFFSET_Y = Math.abs(MAP_MIN_Y >> SHIFT_BY); + public static final int OFFSET_X = Math.abs(WORLD_X_MIN >> SHIFT_BY); + public static final int OFFSET_Y = Math.abs(WORLD_Y_MIN >> SHIFT_BY); /** Number of regions. */ - private static final int REGIONS_X = (MAP_MAX_X >> SHIFT_BY) + OFFSET_X; - private static final int REGIONS_Y = (MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y; + private static final int REGIONS_X = (WORLD_X_MAX >> SHIFT_BY) + OFFSET_X; + private static final int REGIONS_Y = (WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y; /** Map containing all the players in game. */ private static final Map _allPlayers = new ConcurrentHashMap<>(); @@ -817,9 +817,6 @@ public class World return _memberInPartyNumber.get(); } - /** - * @return the current instance of World - */ public static World getInstance() { return SingletonHolder.INSTANCE; diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/WorldObject.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/WorldObject.java index 0e5536bbc0..62313ffea1 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/WorldObject.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/WorldObject.java @@ -188,23 +188,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif synchronized (this) { int spawnX = x; - if (spawnX > World.MAP_MAX_X) + if (spawnX > World.WORLD_X_MAX) { - spawnX = World.MAP_MAX_X - 5000; + spawnX = World.WORLD_X_MAX - 5000; } - if (spawnX < World.MAP_MIN_X) + if (spawnX < World.WORLD_X_MIN) { - spawnX = World.MAP_MIN_X + 5000; + spawnX = World.WORLD_X_MIN + 5000; } int spawnY = y; - if (spawnY > World.MAP_MAX_Y) + if (spawnY > World.WORLD_Y_MAX) { - spawnY = World.MAP_MAX_Y - 5000; + spawnY = World.WORLD_Y_MAX - 5000; } - if (spawnY < World.MAP_MIN_Y) + if (spawnY < World.WORLD_Y_MIN) { - spawnY = World.MAP_MIN_Y + 5000; + spawnY = World.WORLD_Y_MIN + 5000; } // Set the x,y,z position of the WorldObject. If flagged with _isSpawned, setXYZ will automatically update world region, so avoid that. @@ -518,23 +518,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif public void setXYZInvisible(int x, int y, int z) { int correctX = x; - if (correctX > World.MAP_MAX_X) + if (correctX > World.WORLD_X_MAX) { - correctX = World.MAP_MAX_X - 5000; + correctX = World.WORLD_X_MAX - 5000; } - if (correctX < World.MAP_MIN_X) + if (correctX < World.WORLD_X_MIN) { - correctX = World.MAP_MIN_X + 5000; + correctX = World.WORLD_X_MIN + 5000; } int correctY = y; - if (correctY > World.MAP_MAX_Y) + if (correctY > World.WORLD_Y_MAX) { - correctY = World.MAP_MAX_Y - 5000; + correctY = World.WORLD_Y_MAX - 5000; } - if (correctY < World.MAP_MIN_Y) + if (correctY < World.WORLD_Y_MIN) { - correctY = World.MAP_MIN_Y + 5000; + correctY = World.WORLD_Y_MIN + 5000; } setXYZ(correctX, correctY, z); diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/Creature.java index 47987924ee..127e8c7644 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -65,8 +65,6 @@ 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.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -2585,7 +2583,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3406,8 +3404,8 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe final int originalX = x; final int originalY = y; final int originalZ = z; - final int gtx = (originalX - World.MAP_MIN_X) >> 4; - final int gty = (originalY - World.MAP_MIN_Y) >> 4; + final int gtx = (originalX - World.WORLD_X_MIN) >> 4; + final int gty = (originalY - World.WORLD_Y_MIN) >> 4; if (isOnGeodataPath()) { try @@ -3430,7 +3428,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe && !(((curZ - z) > 300) && (distance < 300))) // Prohibit correcting destination if character wants to fall. { // location different if destination wasn't reached (or just z coord is different) - final Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(curX, curY, curZ, x, y, z, getInstanceWorld()); + final Location destiny = GeoEngine.getInstance().getValidLocation(curX, curY, curZ, x, y, z, getInstanceWorld()); x = destiny.getX(); y = destiny.getY(); z = destiny.getZ(); @@ -3444,7 +3442,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { if (isPlayer() && !_isFlying && !isInWater) diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/Summon.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/Summon.java index 6065b24f61..919cda941e 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/Summon.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/actor/Summon.java @@ -106,7 +106,7 @@ public abstract class Summon extends Playable final int x = owner.getX(); final int y = owner.getY(); final int z = owner.getZ(); - final Location location = GeoEngine.getInstance().canMoveToTargetLoc(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, owner.getInstanceWorld()); + final Location location = GeoEngine.getInstance().getValidLocation(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, getInstanceWorld()); setXYZInvisible(location.getX(), location.getY(), location.getZ()); } diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java index abbe632775..8943842d96 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java @@ -1569,7 +1569,7 @@ public class ItemInstance extends WorldObject if (dropper != null) { final Instance instance = dropper.getInstanceWorld(); - final Location dropDest = GeoEngine.getInstance().canMoveToTargetLoc(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); + final Location dropDest = GeoEngine.getInstance().getValidLocation(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); x = dropDest.getX(); y = dropDest.getY(); z = dropDest.getZ(); diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java index 4659b0a0cd..a7066c227f 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java @@ -1179,7 +1179,7 @@ public class SkillCaster implements Runnable } } - final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().canMoveToTargetLoc(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); + final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().getValidLocation(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); creature.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); creature.broadcastPacket(new FlyToLocation(creature, destination, flyType, 0, 0, 333)); diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java index c62c821f44..30068134e3 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java @@ -114,7 +114,7 @@ public class ValidatePosition implements IClientIncomingPacket { if (player.isFalling(_z)) { - final int nearestZ = GeoEngine.getInstance().getHigherHeight(_x, _y, _z); + final int nearestZ = GeoEngine.getInstance().getHeight(_x, _y, _z); if (player.getZ() < nearestZ) { player.setXYZ(_x, _y, nearestZ); diff --git a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/util/GeoUtils.java index 4600a0fc53..19a979819e 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_5.5_EtinasFate/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -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.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; @@ -30,21 +30,21 @@ public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getInstance().getWorldX(iter.x()); - final int wy = GeoEngine.getInstance().getWorldY(iter.y()); + final int wx = GeoEngine.getWorldX(iter.x()); + final int wy = GeoEngine.getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public final class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); - int wy = GeoEngine.getInstance().getWorldY(prevY); + int wx = GeoEngine.getWorldX(prevX); + int wy = GeoEngine.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.getInstance().getWorldX(curX); - wy = GeoEngine.getInstance().getWorldY(curY); + wx = GeoEngine.getWorldX(curX); + wy = GeoEngine.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().checkNearestNswe(x, y, z, nswe)) + if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0) { return Color.GREEN; } @@ -109,9 +109,8 @@ public final class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final GeoEngine ge = GeoEngine.getInstance(); - final int playerGx = ge.getGeoX(player.getX()); - final int playerGy = ge.getGeoY(player.getY()); + final int playerGx = GeoEngine.getGeoX(player.getX()); + final int playerGy = GeoEngine.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -135,32 +134,32 @@ public final class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = ge.getWorldX(gx); - final int y = ge.getWorldY(gy); - final int z = ge.getNearestZ(gx, gy, player.getZ()); + final int x = GeoEngine.getWorldX(gx); + final int y = GeoEngine.getWorldY(gy); + final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); + Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); 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, Cell.NSWE_EAST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); 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, Cell.NSWE_SOUTH); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); 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, Cell.NSWE_WEST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); 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); @@ -188,41 +187,41 @@ public final class GeoUtils { if (y > lastY) { - return Cell.NSWE_SOUTH_EAST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E; } else if (y < lastY) { - return Cell.NSWE_NORTH_EAST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E; } else { - return Cell.NSWE_EAST; + return GeoStructure.CELL_FLAG_E; } } else if (x < lastX) // west { if (y > lastY) { - return Cell.NSWE_SOUTH_WEST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W; } else if (y < lastY) { - return Cell.NSWE_NORTH_WEST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W; } else { - return Cell.NSWE_WEST; + return GeoStructure.CELL_FLAG_W; } } else // unchanged x { if (y > lastY) { - return Cell.NSWE_SOUTH; + return GeoStructure.CELL_FLAG_S; } else if (y < lastY) { - return Cell.NSWE_NORTH; + return GeoStructure.CELL_FLAG_N; } else { diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/config/GeoEngine.ini b/L2J_Mobius_6.0_Fafurion/dist/game/config/GeoEngine.ini index 609e8a89a7..e740c678dd 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_6.0_Fafurion/dist/game/config/GeoEngine.ini @@ -6,33 +6,44 @@ # at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ 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 + # ================================================================= -# Path finding +# Pathfinding # ================================================================= # 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 +# an alternative path (e.g. walk around obstacle), default: True +PathFinding = True -# Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 -PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 +# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 -# Weight for nodes without obstacles far from walls -LowWeight = 0.5 +# Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 +MoveWeight = 10 +MoveWeightDiag = 14 -# Weight for nodes near walls -MediumWeight = 2 +# 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 -# Weight for nodes with obstacles -HighWeight = 3 +# 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. -# Default: LowWeight * sqrt(2) -DiagonalWeight = 0.707 +# Maximum number of generated nodes per one path-finding process, default 3500 +MaxIterations = 3500 # ================================================================= -# Other +# Line of Sight # ================================================================= -# Correct player Z after falling through the ground. -CorrectPlayerZ = False +# 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 diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/geodata/L2D_GeoEngine.patch b/L2J_Mobius_6.0_Fafurion/dist/game/data/geodata/L2D_GeoEngine.patch deleted file mode 100644 index f8577e3a0e..0000000000 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/geodata/L2D_GeoEngine.patch +++ /dev/null @@ -1,6035 +0,0 @@ -Index: dist/game/GeoDataConverter.bat -=================================================================== ---- dist/game/GeoDataConverter.bat (nonexistent) -+++ dist/game/GeoDataConverter.bat (working copy) -@@ -0,0 +1,6 @@ -+@echo off -+title L2D geodata converter -+ -+java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter -+ -+pause -Index: dist/game/GeoDataConverter.sh -=================================================================== ---- dist/game/GeoDataConverter.sh (nonexistent) -+++ dist/game/GeoDataConverter.sh (working copy) -@@ -0,0 +1,4 @@ -+#! /bin/sh -+ -+java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 -+ -Index: dist/game/config/GeoEngine.ini -=================================================================== ---- dist/game/config/GeoEngine.ini (revision 8409) -+++ dist/game/config/GeoEngine.ini (working copy) -@@ -1,6 +1,10 @@ - # ================================================================= - # Geodata - # ================================================================= -+# Because of real-time performance we are using geodata files only in -+# diagonal L2D format now (using filename e.g. 22_16.l2d). -+# L2D geodata can be obtained by conversion of existing L2J or L2OFF geodata. -+# Launch "GeoDataConverter.bat/sh" and follow instructions to start the conversion. - - # 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/ -@@ -13,6 +17,16 @@ - CoordSynchronize = 2 - - # ================================================================= -+# Path checking -+# ================================================================= -+ -+# 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 finding - # ================================================================= - -@@ -23,19 +37,23 @@ - # Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - --# Weight for nodes without obstacles far from walls --LowWeight = 0.5 -+# Base path weight, when moving from one node to another on axis direction, default: 10 -+BaseWeight = 10 - --# Weight for nodes near walls --MediumWeight = 2 -+# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -+DiagonalWeight = 14 - --# Weight for nodes with obstacles --HighWeight = 3 -+# When movement flags of target node is blocked to any direction, multiply movement weight by this multiplier. -+# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 10 -+ObstacleMultiplier = 10 - --# Weight for diagonal movement. --# Default: LowWeight * sqrt(2) --DiagonalWeight = 0.707 -+# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 20 -+# For proper function must be higher than BaseWeight and/or DiagonalWeight. -+HeuristicWeight = 20 - -+# Maximum number of generated nodes per one path-finding process, default 3500 -+MaxIterations = 3500 -+ - # ================================================================= - # Other - # ================================================================= -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (working copy) -@@ -55,9 +55,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -@@ -73,9 +72,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (working copy) -@@ -18,11 +18,11 @@ - - import java.util.List; - --import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -+import org.l2jmobius.gameserver.geoengine.GeoEngine; - import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -+import org.l2jmobius.gameserver.network.SystemMessageId; - import org.l2jmobius.gameserver.util.BuilderUtil; - - public class AdminPathNode implements IAdminCommandHandler -@@ -37,29 +37,30 @@ - { - if (command.equals("admin_path_find")) - { -- if (!Config.PATHFINDING) -- { -- BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); -- return true; -- } - if (activeChar.getTarget() != null) - { -- final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); -+ final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); - if (path == null) - { -- BuilderUtil.sendSysMessage(activeChar, "No Route!"); -- return true; -+ BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); - } -- for (AbstractNodeLoc a : path) -+ else - { -- BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); -+ for (Location point : path) -+ { -+ BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); -+ } - } - } - else - { -- BuilderUtil.sendSysMessage(activeChar, "No Target!"); -+ activeChar.sendPacket(SystemMessageId.INVALID_TARGET); - } - } -+ else -+ { -+ return false; -+ } - return true; - } - -Index: java/org/l2jmobius/Config.java -=================================================================== ---- java/org/l2jmobius/Config.java (revision 8409) -+++ java/org/l2jmobius/Config.java (working copy) -@@ -32,7 +32,6 @@ - import java.net.UnknownHostException; - import java.nio.charset.StandardCharsets; - import java.nio.file.Files; --import java.nio.file.Path; - import java.nio.file.Paths; - import java.time.Duration; - import java.util.ArrayList; -@@ -966,14 +965,17 @@ - // -------------------------------------------------- - // GeoEngine - // -------------------------------------------------- -- public static Path GEODATA_PATH; -+ public static String GEODATA_PATH; -+ public static int COORD_SYNCHRONIZE; -+ public static int PART_OF_CHARACTER_HEIGHT; -+ public static int MAX_OBSTACLE_HEIGHT; - public static boolean PATHFINDING; - public static String PATHFIND_BUFFERS; -- public static float LOW_WEIGHT; -- public static float MEDIUM_WEIGHT; -- public static float HIGH_WEIGHT; -- public static float DIAGONAL_WEIGHT; -- public static int COORD_SYNCHRONIZE; -+ public static int BASE_WEIGHT; -+ public static int DIAGONAL_WEIGHT; -+ public static int HEURISTIC_WEIGHT; -+ public static int OBSTACLE_MULTIPLIER; -+ public static int MAX_ITERATIONS; - public static boolean CORRECT_PLAYER_Z; - - /** Attribute System */ -@@ -2568,14 +2570,17 @@ - - // Load GeoEngine config file (if exists) - final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); -- GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); -+ GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); -+ COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); -+ MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); - PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -- LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); -- MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); -- HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); -- DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); -- COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); -+ DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); -+ OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); -+ HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); -+ MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); - CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); - - // Load AllowedPlayerRaces config file (if exists) -Index: java/org/l2jmobius/gameserver/geoengine/GeoEngine.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (revision 8435) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (working copy) -@@ -16,100 +16,90 @@ - */ - package org.l2jmobius.gameserver.geoengine; - --import java.io.IOException; -+import java.io.File; - import java.io.RandomAccessFile; - import java.nio.ByteOrder; --import java.nio.channels.FileChannel.MapMode; --import java.nio.file.Files; --import java.nio.file.Path; --import java.util.concurrent.atomic.AtomicReferenceArray; --import java.util.logging.Level; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.List; - import java.util.logging.Logger; - - import org.l2jmobius.Config; - import org.l2jmobius.gameserver.data.xml.DoorData; - import org.l2jmobius.gameserver.data.xml.FenceData; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; --import org.l2jmobius.gameserver.geoengine.geodata.IRegion; --import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; --import org.l2jmobius.gameserver.geoengine.geodata.Region; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.World; - import org.l2jmobius.gameserver.model.WorldObject; -+import org.l2jmobius.gameserver.model.actor.Creature; - import org.l2jmobius.gameserver.model.instancezone.Instance; --import org.l2jmobius.gameserver.model.interfaces.ILocational; --import org.l2jmobius.gameserver.util.GeoUtils; --import org.l2jmobius.gameserver.util.LinePointIterator; --import org.l2jmobius.gameserver.util.LinePointIterator3D; -+import org.l2jmobius.gameserver.util.MathUtil; - - /** -- * @author -Nemesiss-, HorridoJoho -+ * @author Hasha - */ - public class GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); -+ protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - -- private static final int WORLD_MIN_X = -655360; -- private static final int WORLD_MIN_Y = -589824; -- private static final int WORLD_MIN_Z = -16384; -+ private final ABlock[][] _blocks; -+ private final BlockNull _nullBlock; - -- /** 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; -- -- /** The regions array */ -- private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); -- -- private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; -- private static final int ELEVATED_SEE_OVER_DISTANCE = 2; -- private static final int MAX_SEE_OVER_HEIGHT = 48; -- -- protected GeoEngine() -+ /** -+ * GeoEngine constructor. Loads all geodata files of chosen geodata format. -+ */ -+ public GeoEngine() - { - LOGGER.info("GeoEngine: Initializing..."); -- for (int i = 0; i < _regions.length(); i++) -- { -- _regions.set(i, NullRegion.INSTANCE); -- } - -+ // initialize block container -+ _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; -+ -+ // load null block -+ _nullBlock = new BlockNull(); -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files according to geoengine config setup - int loaded = 0; -- try -+ long fileSize = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { -- for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { -- for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) -+ final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); -+ if (f.exists() && !f.isDirectory()) - { -- final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); -- if (Files.exists(geoFilePath)) -+ // region file is load-able, try to load it -+ if (loadGeoBlocks(rx, ry)) - { -- try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) -- { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -- loaded++; -- } -+ loaded++; -+ fileSize += f.length(); - } - } -+ else -+ { -+ // region file is not load-able, load null blocks -+ loadNullBlocks(rx, ry); -+ } - } - } -- catch (Exception e) -+ -+ LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -+ if (loaded > 0) - { -- LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); -- System.exit(1); -+ LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); - } - -- LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -- -- // Avoid wrong configs when no files are loaded. -+ // avoid wrong configs when no files are loaded - if (loaded == 0) - { - if (Config.PATHFINDING) -@@ -123,619 +113,820 @@ - LOGGER.info("GeoEngine: Forcing CoordSynchronize setting to -1."); - } - } -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); - } - - /** -- * @param geoX -- * @param geoY -- * @return the region -+ * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. -+ * @return boolean : True, when geodata file was loaded without problem. - */ -- private IRegion getRegion(int geoX, int geoY) -+ private final boolean loadGeoBlocks(int regionX, int regionY) - { -- final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); -- if ((region < 0) || (region >= _regions.length())) -+ final String filename = String.format(GeoFormat.L2D.getFilename(), regionX, regionY); -+ -+ // standard load -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) - { -- return null; -+ // initialize file buffer -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, GeoFormat.L2D); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ } -+ -+ // check data consistency -+ if (buffer.remaining() > 0) -+ { -+ LOGGER.warning("GeoEngine: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ } -+ -+ // loading was successful -+ return true; - } -- return _regions.get(region); -- } -- -- /** -- * @param filePath -- * @param regionX -- * @param regionY -- * @throws IOException -- */ -- 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")) -+ catch (Exception e) - { -- _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -+ // an error occured while loading, load null blocks -+ LOGGER.warning("GeoEngine: Error while loading " + filename + " region file."); -+ LOGGER.warning(e.getMessage()); -+ -+ // replace whole region file with null blocks -+ loadNullBlocks(regionX, regionY); -+ -+ // loading was not successful -+ return false; - } - } - - /** -- * @param regionX -- * @param regionY -+ * Loads null blocks. Used when no region file is detected or an error occurs during loading. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. - */ -- public void unloadRegion(int regionX, int regionY) -+ private final void loadNullBlocks(int regionX, int regionY) - { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); -- } -- -- /** -- * @param geoX -- * @param geoY -- * @return if geodata exist -- */ -- public boolean hasGeoPos(int geoX, int geoY) -- { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // load all null blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { -- return false; -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[blockX + ix][blockY + iy] = _nullBlock; -+ } - } -- return region.hasGeo(); - } - -+ // GEODATA - GENERAL -+ - /** -- * Checks the specified position for available geodata. -- * @param x the world x -- * @param y the world y -- * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise -+ * Converts world X to geodata X. -+ * @param worldX -+ * @return int : Geo X - */ -- public boolean hasGeo(int x, int y) -+ public static int getGeoX(int worldX) - { -- return hasGeoPos(getGeoX(x), getGeoY(y)); -+ return (MathUtil.limit(worldX, World.MAP_MIN_X, World.MAP_MAX_X) - World.MAP_MIN_X) >> 4; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe check -+ * Converts world Y to geodata Y. -+ * @param worldY -+ * @return int : Geo Y - */ -- public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) -+ public static int getGeoY(int worldY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return true; -- } -- return region.checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(worldY, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.MAP_MIN_Y) >> 4; - } - - /** -+ * Converts geodata X to world X. - * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe anti-corner cut -+ * @return int : World X - */ -- public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) -+ public static int getWorldX(int geoX) - { -- boolean can = true; -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- return can && checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(geoX, 0, GeoStructure.GEO_CELLS_X) << 4) + World.MAP_MIN_X + 8; - } - - /** -- * @param geoX -+ * Converts geodata Y to world Y. - * @param geoY -- * @param worldZ -- * @return the nearest Z value -+ * @return int : World Y - */ -- public int getNearestZ(int geoX, int geoY, int worldZ) -+ public static int getWorldY(int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return worldZ; -- } -- return region.getNearestZ(geoX, geoY, worldZ); -+ return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next lower Z value -+ * Returns block of geodata on given coordinates. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return {@link ABlock} : Block of geodata. - */ -- public int getNextLowerZ(int geoX, int geoY, int worldZ) -+ private final ABlock getBlock(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final int x = geoX / GeoStructure.BLOCK_CELLS_X; -+ final int y = geoY / GeoStructure.BLOCK_CELLS_Y; -+ -+ // if x or y is out of array return null -+ if ((x > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) - { -- return worldZ; -+ return _blocks[x][y]; - } -- return region.getNextLowerZ(geoX, geoY, worldZ); -+ return null; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next higher Z value -+ * Check if geo coordinates has geo. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return boolean : True, if given geo coordinates have geodata - */ -- public int getNextHigherZ(int geoX, int geoY, int worldZ) -+ public boolean hasGeoPos(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final ABlock block = getBlock(geoX, geoY); -+ if (block == null) // null block check - { -- return worldZ; -+ // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) -+ // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); -+ return false; - } -- return region.getNextHigherZ(geoX, geoY, worldZ); -+ return block.hasGeoPos(); - } - - /** -- * Gets the Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the height of cell, which is closest to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, closest to given coordinates. - */ -- public int getHeight(int x, int y, int z) -+ public short getHeightNearest(int geoX, int geoY, int worldZ) - { -- return getNearestZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getHeightNearest(geoX, geoY, worldZ) : (short) worldZ; - } - - /** -- * Gets the next lower Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the NSWE flag byte of cell, which is closes to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. - */ -- public int getLowerHeight(int x, int y, int z) -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) - { -- return getNextLowerZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getNsweNearest(geoX, geoY, worldZ) : (byte) 0xFF; - } - - /** -- * Gets the next higher Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Check if world coordinates has geo. -+ * @param worldX : World X -+ * @param worldY : World Y -+ * @return boolean : True, if given world coordinates have geodata - */ -- public int getHigherHeight(int x, int y, int z) -+ public boolean hasGeo(int worldX, int worldY) - { -- return getNextHigherZ(getGeoX(x), getGeoY(y), z); -+ return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); - } - - /** -- * @param worldX -- * @return the geo X -+ * Returns closest Z coordinate according to geodata. -+ * @param worldX : world x -+ * @param worldY : world y -+ * @param worldZ : world z -+ * @return short : nearest Z coordinates according to geodata - */ -- public int getGeoX(int worldX) -+ public short getHeight(int worldX, int worldY, int worldZ) - { -- return (worldX - WORLD_MIN_X) / 16; -+ return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - -- /** -- * @param worldY -- * @return the geo Y -- */ -- public int getGeoY(int worldY) -- { -- return (worldY - WORLD_MIN_Y) / 16; -- } -+ // PATHFINDING - - /** -- * @param worldZ -- * @return the geo Z -+ * Check line of sight from {@link WorldObject} to {@link WorldObject}. -+ * @param origin : The origin object. -+ * @param target : The target object. -+ * @return {@code boolean} : True if origin can see target - */ -- public int getGeoZ(int worldZ) -+ public boolean canSeeTarget(WorldObject origin, WorldObject target) - { -- return (worldZ - WORLD_MIN_Z) / 16; -- } -- -- /** -- * @param geoX -- * @return the world X -- */ -- public int getWorldX(int geoX) -- { -- return (geoX * 16) + WORLD_MIN_X + 8; -- } -- -- /** -- * @param geoY -- * @return the world Y -- */ -- public int getWorldY(int geoY) -- { -- return (geoY * 16) + WORLD_MIN_Y + 8; -- } -- -- /** -- * @param geoZ -- * @return the world Z -- */ -- public int getWorldZ(int geoZ) -- { -- return (geoZ * 16) + WORLD_MIN_Z + 8; -- } -- -- /** -- * Can see target. Doors as target always return true. Checks doors between. -- * @param cha the character -- * @param target the target -- * @return {@code true} if the character can see the target (LOS), {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, WorldObject target) -- { -- if (target.isDoor()) -+ if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) - { -- // Can always see doors. - return true; - } -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param cha the character -- * @param worldPosition the world position -- * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) -- { -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param world -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @param tworld the target's instanceId -- * @return -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) -- { -- return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instance -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) -- { -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) -+ -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = target.getX(); -+ final int ty = target.getY(); -+ final int tz = target.getZ(); -+ -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } -- return canSeeTarget(x, y, z, tx, ty, tz); -- } -- -- /** -- * @param prevX -- * @param prevY -- * @param prevGeoZ -- * @param curX -- * @param curY -- * @param nswe -- * @return the LOS Z value -- */ -- private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) -- { -- if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { -- throw new RuntimeException("Multiple directions!"); -+ return false; - } - -- if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- return getNearestZ(curX, curY, prevGeoZ); -+ return true; - } - -- return getNextHigherZ(curX, curY, prevGeoZ); -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) -+ { -+ return true; -+ } -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) -+ { -+ return goz == gtz; -+ } -+ -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) -+ { -+ oheight = ((Creature) origin).getCollisionHeight() * 2; -+ } -+ -+ double theight = 0; -+ if (target.isCreature()) -+ { -+ theight = ((Creature) target).getCollisionHeight() * 2; -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); - } - - /** -- * Can see target. Does not check doors between. -- * @param xValue the x coordinate -- * @param yValue the y coordinate -- * @param zValue the z coordinate -- * @param txValue the target's x coordinate -- * @param tyValue the target's y coordinate -- * @param tzValue the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -+ * Check line of sight from {@link WorldObject} to {@link Location}. -+ * @param origin : The origin object. -+ * @param position : The target position. -+ * @return {@code boolean} : True if object can see position - */ -- public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) -+ public boolean canSeeTarget(WorldObject origin, Location position) - { -- int x = xValue; -- int y = yValue; -- int tx = txValue; -- int ty = tyValue; -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = position.getX(); -+ final int ty = position.getY(); -+ final int tz = position.getZ(); - -- int geoX = getGeoX(x); -- int geoY = getGeoY(y); -- int tGeoX = getGeoX(tx); -- int tGeoY = getGeoY(ty); -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) -+ { -+ return false; -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) -+ { -+ return false; -+ } - -- int z = getNearestZ(geoX, geoY, zValue); -- int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- // Fastpath. -- if ((geoX == tGeoX) && (geoY == tGeoY)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- if (hasGeoPos(tGeoX, tGeoY)) -- { -- return z == tz; -- } - return true; - } - -- if (tz > z) -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) - { -- int tmp = tx; -- tx = x; -- x = tmp; -- -- tmp = ty; -- ty = y; -- y = tmp; -- -- tmp = tz; -- tz = z; -- z = tmp; -- -- tmp = tGeoX; -- tGeoX = geoX; -- geoX = tmp; -- -- tmp = tGeoY; -- tGeoY = geoY; -- geoY = tmp; -+ return goz == gtz; - } - -- final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); -- // First point is guaranteed to be available, skip it, we can always see our own position. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- final int prevZ = pointIter.z(); -- int prevGeoZ = prevZ; -- int ptIndex = 0; -- while (pointIter.next()) -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight(); -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); -+ } -+ -+ /** -+ * Simple check for origin to target visibility. -+ * @param goxValue : origin X geodata coordinate -+ * @param goyValue : origin Y geodata coordinate -+ * @param gozValue : origin Z geodata coordinate -+ * @param oheight : origin height (if instance of {@link Character}) -+ * @param gtxValue : target X geodata coordinate -+ * @param gtyValue : target Y geodata coordinate -+ * @param gtzValue : target Z geodata coordinate -+ * @param theight : target height (if instance of {@link Character}) -+ * @param instance -+ * @return {@code boolean} : True, when target can be seen. -+ */ -+ private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) -+ { -+ int goz = gozValue; -+ int gtz = gtzValue; -+ int gox = goxValue; -+ int goy = goyValue; -+ int gtx = gtxValue; -+ int gty = gtyValue; -+ -+ // get line of sight Z coordinates -+ double losoz = goz + ((oheight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ double lostz = gtz + ((theight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ -+ // get X delta and signum -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirox = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; -+ final byte dirtx = sx > 0 ? GeoStructure.CELL_FLAG_W : GeoStructure.CELL_FLAG_E; -+ -+ // get Y delta and signum -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte diroy = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ final byte dirty = sy > 0 ? GeoStructure.CELL_FLAG_N : GeoStructure.CELL_FLAG_S; -+ -+ // get Z delta -+ final int dm = Math.max(dx, dy); -+ final double dz = (lostz - losoz) / dm; -+ -+ // get direction flag for diagonal movement -+ final byte diroxy = getDirXY(dirox, diroy); -+ final byte dirtxy = getDirXY(dirtx, dirty); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte diro; -+ byte dirt; -+ -+ // initialize node values -+ int nox = gox; -+ int noy = goy; -+ int ntx = gtx; -+ int nty = gty; -+ byte nsweo = getNsweNearest(gox, goy, goz); -+ byte nswet = getNsweNearest(gtx, gty, gtz); -+ -+ // loop -+ ABlock block; -+ int index; -+ for (int i = 0; i < ((dm + 1) / 2); i++) -+ { -+ // reset direction flag -+ diro = 0; -+ dirt = 0; - -- if ((curX == prevX) && (curY == prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- continue; -+ // calculate next point XY coordinates -+ d -= dy; -+ d += dx; -+ nox += sx; -+ ntx -= sx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroxy; -+ dirt |= dirtxy; - } -+ else if (e2 > -dy) -+ { -+ // calculate next point X coordinate -+ d -= dy; -+ nox += sx; -+ ntx -= sx; -+ diro |= dirox; -+ dirt |= dirtx; -+ } -+ else if (e2 < dx) -+ { -+ // calculate next point Y coordinate -+ d += dx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroy; -+ dirt |= dirty; -+ } - -- final int beeCurZ = pointIter.z(); -- int curGeoZ = prevGeoZ; -- -- // Check if the position has geodata. -- if (hasGeoPos(curX, curY)) - { -- final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); -- curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); -- int maxHeight; -- if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) -+ // get block of the next cell -+ block = getBlock(nox, noy); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nsweo & diro) == 0) - { -- maxHeight = z + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); - } - else - { -- maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); - } - -- boolean canSeeThrough = false; -- if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) -+ // layer does not exist, return -+ if (index == -1) - { -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else -- { -- canSeeThrough = true; -- } -+ return false; - } - -- if (!canSeeThrough) -+ // get layer and next line of sight Z coordinate -+ goz = block.getHeight(index); -+ losoz += dz; -+ -+ // perform line of sight check, return when fails -+ if ((goz - losoz) > Config.MAX_OBSTACLE_HEIGHT) - { - return false; - } -+ -+ // get layer nswe -+ nsweo = block.getNswe(index); - } -+ { -+ // get block of the next cell -+ block = getBlock(ntx, nty); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nswet & dirt) == 0) -+ { -+ index = block.getIndexAbove(ntx, nty, gtz - GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ else -+ { -+ index = block.getIndexBelow(ntx, nty, gtz + GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ -+ // layer does not exist, return -+ if (index == -1) -+ { -+ return false; -+ } -+ -+ // get layer and next line of sight Z coordinate -+ gtz = block.getHeight(index); -+ lostz -= dz; -+ -+ // perform line of sight check, return when fails -+ if ((gtz - lostz) > Config.MAX_OBSTACLE_HEIGHT) -+ { -+ return false; -+ } -+ -+ // get layer nswe -+ nswet = block.getNswe(index); -+ } - -- prevX = curX; -- prevY = curY; -- prevGeoZ = curGeoZ; -- ++ptIndex; -+ // update coords -+ gox = nox; -+ goy = noy; -+ gtx = ntx; -+ gty = nty; - } - -- return true; -+ // when iteration is completed, compare final Z coordinates -+ return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); - } - - /** -- * Move check. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param zValue the z coordinate -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tzValue the target's z coordinate -- * @param instance the instance -- * @return the last Location (x,y,z) where player can walk - just before wall -+ * Check movement from coordinates to coordinates. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {code boolean} : True if target coordinates are reachable from origin coordinates - */ -- public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) -+ public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- final int geoX = getGeoX(x); -- final int geoY = getGeoY(y); -- final int z = getNearestZ(geoX, geoY, zValue); -- final int tGeoX = getGeoX(tx); -- final int tGeoY = getGeoY(ty); -- final int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } -- if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = z; -+ // perform geodata check -+ final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); -+ return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); -+ } -+ -+ /** -+ * Check movement from origin to target. Returns last available point in the checked path. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {@link Location} : Last point where object can walk (just before wall) -+ */ -+ public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) -+ { -+ return new Location(ox, oy, oz); -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) -+ { -+ return new Location(ox, oy, oz); -+ } - -- while (pointIter.next()) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -- -- if (hasGeoPos(prevX, prevY)) -- { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -- { -- // Can't move, return previous location. -- return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); -- } -- } -- -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return new Location(tx, ty, tz); - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != tz)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- // Different floors, return start location. -- return new Location(x, y, z); -+ return new Location(tx, ty, tz); - } - -- return new Location(tx, ty, tz); -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) -+ { -+ return new Location(tx, ty, tz); -+ } -+ -+ // perform geodata check -+ return checkMove(gox, goy, goz, gtx, gty, gtz, instance); - } - - /** -- * Checks if its possible to move from one location to another. -- * @param fromX the X coordinate to start checking from -- * @param fromY the Y coordinate to start checking from -- * @param fromZvalue the Z coordinate to start checking from -- * @param toX the X coordinate to end checking at -- * @param toY the Y coordinate to end checking at -- * @param toZvalue the Z coordinate to end checking at -- * @param instance the instance -- * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise -+ * With this method you can check if a position is visible or can be reached by beeline movement.
-+ * Target X and Y reachable and Z is on same floor: -+ *
    -+ *
  • Location of the target with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y reachable but Z is on another floor: -+ *
    -+ *
  • Location of the origin with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y not reachable: -+ *
    -+ *
  • Last accessible location in destination to target.
  • -+ *
-+ * @param gox : origin X geodata coordinate -+ * @param goy : origin Y geodata coordinate -+ * @param goz : origin Z geodata coordinate -+ * @param gtx : target X geodata coordinate -+ * @param gty : target Y geodata coordinate -+ * @param gtz : target Z geodata coordinate -+ * @param instance -+ * @return {@link GeoLocation} : The last allowed point of movement. - */ -- public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) -+ protected final GeoLocation checkMove(int gox, int goy, int goz, int gtx, int gty, int gtz, Instance instance) - { -- final int geoX = getGeoX(fromX); -- final int geoY = getGeoY(fromY); -- final int fromZ = getNearestZ(geoX, geoY, fromZvalue); -- final int tGeoX = getGeoX(toX); -- final int tGeoY = getGeoY(toY); -- final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); -- -- if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) -+ if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } -- if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) -+ if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = fromZ; -+ // get X delta, signum and direction flag -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirX = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; - -- while (pointIter.next()) -+ // get Y delta, signum and direction flag -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte dirY = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ -+ // get direction flag for diagonal movement -+ final byte dirXY = getDirXY(dirX, dirY); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte direction; -+ -+ // load pointer coordinates -+ int gpx = gox; -+ int gpy = goy; -+ int gpz = goz; -+ -+ // load next pointer -+ int nx = gpx; -+ int ny = gpy; -+ -+ // loop -+ int count = 0; -+ while (count++ < Config.MAX_ITERATIONS) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -+ direction = 0; - -- if (hasGeoPos(prevX, prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -+ d -= dy; -+ d += dx; -+ nx += sx; -+ ny += sy; -+ direction |= dirXY; -+ } -+ else if (e2 > -dy) -+ { -+ d -= dy; -+ nx += sx; -+ direction |= dirX; -+ } -+ else if (e2 < dx) -+ { -+ d += dx; -+ ny += sy; -+ direction |= dirY; -+ } -+ -+ // obstacle found, return -+ if ((getNsweNearest(gpx, gpy, gpz) & direction) == 0) -+ { -+ return new GeoLocation(gpx, gpy, gpz); -+ } -+ -+ // update pointer coordinates -+ gpx = nx; -+ gpy = ny; -+ gpz = getHeightNearest(nx, ny, gpz); -+ -+ // target coordinates reached -+ if ((gpx == gtx) && (gpy == gty)) -+ { -+ if (gpz == gtz) - { -- return false; -+ // path found, Z coordinates are okay, return target point -+ return new GeoLocation(gtx, gty, gtz); - } -+ -+ // path found, Z coordinates are not okay, return last good point -+ return new GeoLocation(gpx, gpy, gpz); - } -+ } -+ -+ return new GeoLocation(gox, goy, goz); -+ } -+ -+ /** -+ * Returns diagonal NSWE flag format of combined two NSWE flags. -+ * @param dirX : X direction NSWE flag -+ * @param dirY : Y direction NSWE flag -+ * @return byte : NSWE flag of combined direction -+ */ -+ private static byte getDirXY(byte dirX, byte dirY) -+ { -+ // check axis directions -+ if (dirY == GeoStructure.CELL_FLAG_N) -+ { -+ if (dirX == GeoStructure.CELL_FLAG_W) -+ { -+ return GeoStructure.CELL_FLAG_NW; -+ } - -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return GeoStructure.CELL_FLAG_NE; - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) -+ if (dirX == GeoStructure.CELL_FLAG_W) - { -- // Different floors. -- return false; -+ return GeoStructure.CELL_FLAG_SW; - } - -- return true; -+ return GeoStructure.CELL_FLAG_SE; - } - -+ /** -+ * Returns the list of location objects as a result of complete path calculation. -+ * @param ox : origin x -+ * @param oy : origin y -+ * @param oz : origin z -+ * @param tx : target x -+ * @param ty : target y -+ * @param tz : target z -+ * @param instance -+ * @return {@code List} : complete path from nodes -+ */ -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ return null; -+ } -+ -+ /** -+ * Returns the instance of the {@link GeoEngine}. -+ * @return {@link GeoEngine} : The instance. -+ */ - public static GeoEngine getInstance() - { - return SingletonHolder.INSTANCE; -@@ -743,6 +934,6 @@ - - private static class SingletonHolder - { -- protected static final GeoEngine INSTANCE = new GeoEngine(); -+ protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); - } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (working copy) -@@ -20,88 +20,84 @@ - import java.util.LinkedList; - import java.util.List; - import java.util.ListIterator; --import java.util.logging.Level; --import java.util.logging.Logger; - - import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; --import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; --import org.l2jmobius.gameserver.model.World; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -+import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.instancezone.Instance; - - /** -- * @author -Nemesiss- -+ * @author Hasha - */ --public class GeoEnginePathfinding -+final class GeoEnginePathfinding extends GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); -+ // pre-allocated buffers -+ private final BufferHolder[] _buffers; - -- private BufferInfo[] _buffers; -- - protected GeoEnginePathfinding() - { -- try -+ super(); -+ -+ final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ _buffers = new BufferHolder[array.length]; -+ int count = 0; -+ for (int i = 0; i < array.length; i++) - { -- final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ final String buf = array[i]; -+ final String[] args = buf.split("x"); - -- _buffers = new BufferInfo[array.length]; -- -- String buf; -- String[] args; -- for (int i = 0; i < array.length; i++) -+ try - { -- buf = array[i]; -- args = buf.split("x"); -- if (args.length != 2) -- { -- throw new Exception("Invalid buffer definition: " + buf); -- } -- -- _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); -+ final int size = Integer.parseInt(args[1]); -+ count += size; -+ _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); - } -+ catch (Exception e) -+ { -+ LOGGER.warning("GeoEnginePathfinding: Can not load buffer setting: " + buf); -+ } - } -- catch (Exception e) -- { -- LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); -- throw new Error("CellPathFinding: load aborted"); -- } -+ -+ LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); - } - -- public boolean pathNodesExist(short regionoffset) -+ @Override -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- return false; -- } -- -- public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) -- { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -- if (!GeoEngine.getInstance().hasGeo(x, y)) -+ // get origin and check existing geo coords -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { - 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)) -+ -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coords -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { - 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)))); -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // Prepare buffer for pathfinding calculations -+ final NodeBuffer buffer = getBuffer(64 + (2 * Math.max(Math.abs(gox - gtx), Math.abs(goy - gty)))); - if (buffer == null) - { - return null; - } - -- List path = null; -+ // find path -+ List path = null; - try - { -- final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); -- -+ final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); - if (result == null) - { - return null; -@@ -125,33 +121,45 @@ - return path; - } - -- int currentX, currentY, currentZ; -- ListIterator middlePoint; -+ // get path list iterator -+ final ListIterator point = path.listIterator(); - -- middlePoint = path.listIterator(); -- currentX = x; -- currentY = y; -- currentZ = z; -+ // get node A (origin) -+ int nodeAx = gox; -+ int nodeAy = goy; -+ short nodeAz = goz; - -- while (middlePoint.hasNext()) -+ // get node B -+ GeoLocation nodeB = (GeoLocation) point.next(); -+ -+ // iterate thought the path to optimize it -+ int count = 0; -+ while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) - { -- final AbstractNodeLoc locMiddle = middlePoint.next(); -- if (!middlePoint.hasNext()) -- { -- break; -- } -+ // get node C -+ final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - -- final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); -- if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) -+ // check movement from node A to node C -+ final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); -+ if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) - { -- middlePoint.remove(); -+ // can move from node A to node C -+ -+ // remove node B -+ point.remove(); - } - else - { -- currentX = locMiddle.getX(); -- currentY = locMiddle.getY(); -- currentZ = locMiddle.getZ(); -+ // can not move from node A to node C -+ -+ // set node A (node B is part of path, update A coordinates) -+ nodeAx = nodeB.getGeoX(); -+ nodeAy = nodeB.getGeoY(); -+ nodeAz = (short) nodeB.getZ(); - } -+ -+ // set node B -+ nodeB = (GeoLocation) point.next(); - } - - return path; -@@ -158,144 +166,102 @@ - } - - /** -- * Convert geodata position to pathnode position -- * @param geo_pos -- * @return pathnode position -+ * Create list of node locations as result of calculated buffer node tree. -+ * @param node : the entry point -+ * @return List : list of node location - */ -- public short getNodePos(int geo_pos) -+ private static List constructPath(Node node) - { -- return (short) (geo_pos >> 3); // OK? -- } -- -- /** -- * Convert node position to pathnode block position -- * @param node_pos -- * @return pathnode block position (0...255) -- */ -- public short getNodeBlock(int node_pos) -- { -- return (short) (node_pos % 256); -- } -- -- public byte getRegionX(int node_pos) -- { -- return (byte) ((node_pos >> 8) + World.TILE_X_MIN); -- } -- -- public byte getRegionY(int node_pos) -- { -- return (byte) ((node_pos >> 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 node_x rx -- * @return -- */ -- public int calculateWorldX(short node_x) -- { -- return World.MAP_MIN_X + (node_x * 128) + 48; -- } -- -- /** -- * Convert pathnode y to World y position -- * @param node_y -- * @return -- */ -- public int calculateWorldY(short node_y) -- { -- return World.MAP_MIN_Y + (node_y * 128) + 48; -- } -- -- private List constructPath(AbstractNode nodeValue) -- { -- final LinkedList path = new LinkedList<>(); -- int previousDirectionX = Integer.MIN_VALUE; -- int previousDirectionY = Integer.MIN_VALUE; -- int directionX, directionY; -+ // create empty list -+ final LinkedList list = new LinkedList<>(); - -- AbstractNode node = nodeValue; -- while (node.getParent() != null) -+ // set direction X/Y -+ int dx = 0; -+ int dy = 0; -+ -+ // get target parent -+ Node target = node; -+ Node parent = target.getParent(); -+ -+ // while parent exists -+ int count = 0; -+ while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { -- directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); -- directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); -+ // get parent <> target direction X/Y -+ final int nx = parent.getLoc().getGeoX() - target.getLoc().getGeoX(); -+ final int ny = parent.getLoc().getGeoY() - target.getLoc().getGeoY(); - -- // only add a new route point if moving direction changes -- if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) -+ // direction has changed? -+ if ((dx != nx) || (dy != ny)) - { -- previousDirectionX = directionX; -- previousDirectionY = directionY; -+ // add node to the beginning of the list -+ list.addFirst(target.getLoc()); - -- path.addFirst(node.getLoc()); -- node.setLoc(null); -+ // update direction X/Y -+ dx = nx; -+ dy = ny; - } - -- node = node.getParent(); -+ // move to next node, set target and get its parent -+ target = parent; -+ parent = target.getParent(); - } - -- return path; -+ // return list -+ return list; - } - -- private CellNodeBuffer alloc(int size) -+ /** -+ * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer. -+ * @param size : pre-calculated minimal required size -+ * @return NodeBuffer : buffer -+ */ -+ private final NodeBuffer getBuffer(int size) - { -- CellNodeBuffer current = null; -- for (BufferInfo i : _buffers) -+ NodeBuffer current = null; -+ for (BufferHolder holder : _buffers) - { -- if (i.mapSize >= size) -+ // Find proper size of buffer -+ if (holder._size < size) - { -- for (CellNodeBuffer buf : i.buffer) -+ continue; -+ } -+ -+ // Find unlocked NodeBuffer -+ for (NodeBuffer buffer : holder._buffer) -+ { -+ if (!buffer.isLocked()) - { -- if (buf.lock()) -- { -- current = buf; -- break; -- } -+ continue; - } -- if (current != null) -- { -- break; -- } - -- // not found, allocate temporary buffer -- current = new CellNodeBuffer(i.mapSize); -- current.lock(); -- if (i.buffer.size() < i.count) -- { -- i.buffer.add(current); -- break; -- } -+ return buffer; - } -+ -+ // NodeBuffer not found, allocate temporary buffer -+ current = new NodeBuffer(holder._size); -+ current.isLocked(); - } - - return current; - } - -- private static final class BufferInfo -+ /** -+ * NodeBuffer container with specified size and count of separate buffers. -+ */ -+ private static final class BufferHolder - { -- final int mapSize; -- final int count; -- ArrayList buffer; -+ final int _size; -+ List _buffer; - -- public BufferInfo(int size, int cnt) -+ public BufferHolder(int size, int count) - { -- mapSize = size; -- count = cnt; -- buffer = new ArrayList<>(count); -+ _size = size; -+ _buffer = new ArrayList<>(count); -+ for (int i = 0; i < count; i++) -+ { -+ _buffer.add(new NodeBuffer(size)); -+ } - } - } -- -- public static GeoEnginePathfinding getInstance() -- { -- return SingletonHolder.INSTANCE; -- } -- -- private static class SingletonHolder -- { -- protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); -- } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (working copy) -@@ -0,0 +1,197 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @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 height of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getHeightNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first above given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, above given coordinates. -+ */ -+ public abstract short getHeightAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first below given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is closest to given coordinates. -+ * @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 the NSWE flag byte of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getNsweNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first above given coordinates. -+ * @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 getNsweAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first below given coordinates. -+ * @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 getNsweBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is closes layer to given coordinates. -+ * @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. -+ * @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 above given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is first layer below given coordinates. -+ * @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 index to data of the cell, which is first layer below given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexBelowOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeight(int index); -+ -+ /** -+ * Returns the height of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightOriginal(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNswe(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNsweOriginal(int index); -+ -+ /** -+ * Sets the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @param nswe : New NSWE flag byte. -+ */ -+ public abstract void setNswe(int index, byte nswe); -+ -+ /** -+ * Saves the block in L2D format to {@link BufferedOutputStream}. Used only for L2D geodata conversion. -+ * @param stream : The stream. -+ * @throws IOException : Can't save the block to steam. -+ */ -+ public abstract void saveBlock(BufferedOutputStream stream) throws IOException; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (working copy) -@@ -0,0 +1,252 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockComplex(ByteBuffer bb, GeoFormat format) -+ { -+ // initialize buffer -+ _buffer = new byte[GeoStructure.BLOCK_CELLS * 3]; -+ -+ // load data -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // 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); -+ } -+ else -+ { -+ // get nswe -+ final byte nswe = bb.get(); -+ _buffer[i * 3] = nswe; -+ -+ // get height -+ final short height = bb.getShort(); -+ _buffer[(i * 3) + 1] = (byte) (height & 0x00FF); -+ _buffer[(i * 3) + 2] = (byte) (height >> 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height > worldZ ? height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height < worldZ ? height : Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public 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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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 int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_COMPLEX_L2D); -+ -+ // write block data -+ stream.write(_buffer, 0, GeoStructure.BLOCK_CELLS * 3); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (working copy) -@@ -0,0 +1,176 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockFlat extends ABlock -+{ -+ protected final short _height; -+ protected byte _nswe; -+ -+ /** -+ * Creates FlatBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockFlat(ByteBuffer bb, GeoFormat format) -+ { -+ _height = bb.getShort(); -+ _nswe = format != GeoFormat.L2D ? 0x0F : (byte) (0xFF); -+ if (format == GeoFormat.L2OFF) -+ { -+ bb.getShort(); -+ } -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return true; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height > worldZ ? _height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height < worldZ ? _height : Short.MAX_VALUE; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height > worldZ ? _nswe : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height < worldZ ? _nswe : 0; -+ } -+ -+ @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return index -+ return _height < worldZ ? 0 : -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _nswe = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_FLAT_L2D); -+ -+ // write height -+ stream.write((byte) (_height & 0x00FF)); -+ stream.write((byte) (_height >> 8)); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (working copy) -@@ -0,0 +1,465 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+import java.nio.ByteOrder; -+import java.util.Arrays; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockMultilayer extends ABlock -+{ -+ private static final int MAX_LAYERS = Byte.MAX_VALUE; -+ -+ private static ByteBuffer _temp; -+ -+ /** -+ * Initializes the temporarily buffer. -+ */ -+ public static void initialize() -+ { -+ // initialize temporarily buffer and sorting mechanism -+ _temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3); -+ _temp.order(ByteOrder.LITTLE_ENDIAN); -+ } -+ -+ /** -+ * Releases temporarily buffer. -+ */ -+ public static void release() -+ { -+ _temp = null; -+ } -+ -+ protected byte[] _buffer; -+ -+ /** -+ * Implicit constructor for children class. -+ */ -+ protected BlockMultilayer() -+ { -+ _buffer = null; -+ } -+ -+ /** -+ * Creates MultilayerBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockMultilayer(ByteBuffer bb, GeoFormat format) -+ { -+ // 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 = format != GeoFormat.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++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // get data -+ final short data = bb.getShort(); -+ -+ // add nswe and height -+ _temp.put((byte) (data & 0x000F)); -+ _temp.putShort((short) ((short) (data & 0xFFF0) >> 1)); -+ } -+ else -+ { -+ // add nswe -+ _temp.put(bb.get()); -+ -+ // add height -+ _temp.putShort(bb.getShort()); -+ } -+ } -+ } -+ -+ // 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 height -+ if (height > worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, return minimum value -+ return Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 height -+ if (height < worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, return maximum value -+ return Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 nswe -+ if (height > worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 nswe -+ if (height < worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @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 first layer data (first from bottom) -+ byte layers = _buffer[index++]; -+ -+ // loop though all cell layers, find closest layer -+ 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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ // get height -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(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]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ // get nswe -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ // set nswe -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_MULTILAYER_L2D); -+ -+ // for each cell -+ int index = 0; -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ // write layers count -+ final byte layers = _buffer[index++]; -+ stream.write(layers); -+ -+ // write cell data -+ stream.write(_buffer, index, layers * 3); -+ -+ // move index to next cell -+ index += layers * 3; -+ } -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (working copy) -@@ -0,0 +1,150 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockNull extends ABlock -+{ -+ private final byte _nswe; -+ -+ public BlockNull() -+ { -+ _nswe = (byte) 0xFF; -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return false; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweBelow(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) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) -+ { -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (nonexistent) -@@ -1,48 +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 . -- */ --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() -- { -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (nonexistent) -@@ -1,77 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (nonexistent) -@@ -1,56 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (working copy) -@@ -0,0 +1,39 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public enum GeoFormat -+{ -+ L2J("%d_%d.l2j"), -+ L2OFF("%d_%d_conv.dat"), -+ L2D("%d_%d.l2d"); -+ -+ private final String _filename; -+ -+ private GeoFormat(String filename) -+ { -+ _filename = filename; -+ } -+ -+ public String getFilename() -+ { -+ return _filename; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (working copy) -@@ -0,0 +1,67 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.geoengine.GeoEngine; -+import org.l2jmobius.gameserver.model.Location; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoLocation extends Location -+{ -+ private byte _nswe; -+ -+ public GeoLocation(int x, int y, int z) -+ { -+ super(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public void set(int x, int y, short z) -+ { -+ super.setXYZ(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public int getGeoX() -+ { -+ return _x; -+ } -+ -+ public int getGeoY() -+ { -+ return _y; -+ } -+ -+ @Override -+ public int getX() -+ { -+ return GeoEngine.getWorldX(_x); -+ } -+ -+ @Override -+ public int getY() -+ { -+ return GeoEngine.getWorldY(_y); -+ } -+ -+ public byte getNSWE() -+ { -+ return _nswe; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (working copy) -@@ -0,0 +1,70 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoStructure -+{ -+ // cells -+ public static final byte CELL_FLAG_E = 1 << 0; -+ public static final byte CELL_FLAG_W = 1 << 1; -+ public static final byte CELL_FLAG_S = 1 << 2; -+ public static final byte CELL_FLAG_N = 1 << 3; -+ public static final byte CELL_FLAG_SE = 1 << 4; -+ public static final byte CELL_FLAG_SW = 1 << 5; -+ public static final byte CELL_FLAG_NE = 1 << 6; -+ public static final byte CELL_FLAG_NW = (byte) (1 << 7); -+ -+ public static final int CELL_HEIGHT = 8; -+ public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6; -+ -+ // blocks -+ public static final byte TYPE_FLAT_L2J_L2OFF = 0; -+ public static final byte TYPE_FLAT_L2D = (byte) 0xD0; -+ public static final byte TYPE_COMPLEX_L2J = 1; -+ public static final byte TYPE_COMPLEX_L2OFF = 0x40; -+ public static final byte TYPE_COMPLEX_L2D = (byte) 0xD1; -+ 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) -+ public static final byte TYPE_MULTILAYER_L2D = (byte) 0xD2; -+ -+ 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; -+ -+ // regions -+ 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; -+ -+ // global geodata -+ private static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); -+ private 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; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (nonexistent) -@@ -1,42 +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 . -- */ --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); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (working copy) -@@ -0,0 +1,53 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public interface IGeoObject -+{ -+ /** -+ * Returns geodata X coordinate of the {@link IGeoObject}. -+ * @return int : Geodata X coordinate. -+ */ -+ int getGeoX(); -+ -+ /** -+ * Returns geodata Y coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Y coordinate. -+ */ -+ int getGeoY(); -+ -+ /** -+ * Returns geodata Z coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Z coordinate. -+ */ -+ int getGeoZ(); -+ -+ /** -+ * Returns height of the {@link IGeoObject}. -+ * @return int : Height. -+ */ -+ int getHeight(); -+ -+ /** -+ * Returns {@link IGeoObject} data. -+ * @return byte[][] : {@link IGeoObject} data. -+ */ -+ byte[][] getObjectGeoData(); -+} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (nonexistent) -@@ -1,47 +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 . -- */ --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(); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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); -- -- // 1 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (nonexistent) -@@ -1,55 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --/** -- * @author HorridoJoho -- */ --public final class NullRegion implements IRegion --{ -- public static final NullRegion INSTANCE = new NullRegion(); -- -- @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 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Region.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (nonexistent) -@@ -1,92 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (nonexistent) -@@ -1,87 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public abstract class AbstractNode --{ -- private T _loc; -- private AbstractNode _parent; -- -- public AbstractNode(T loc) -- { -- _loc = loc; -- } -- -- public void setParent(AbstractNode p) -- { -- _parent = p; -- } -- -- public AbstractNode getParent() -- { -- return _parent; -- } -- -- public T getLoc() -- { -- return _loc; -- } -- -- public void setLoc(T l) -- { -- _loc = l; -- } -- -- @Override -- public int hashCode() -- { -- final int prime = 31; -- int result = 1; -- result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); -- return result; -- } -- -- @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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (nonexistent) -@@ -1,33 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --/** -- * @author -Nemesiss- -- */ --public abstract class AbstractNodeLoc --{ -- public abstract int getX(); -- -- public abstract int getY(); -- -- public abstract int getZ(); -- -- public abstract int getNodeX(); -- -- public abstract int getNodeY(); --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (nonexistent) -@@ -1,67 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public class CellNode extends AbstractNode --{ -- 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (nonexistent) -@@ -1,348 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --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 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) -- { -- _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(); -- } -- -- public final List debugPath() -- { -- final List 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 void getNeighbors() -- { -- if (!_current.getLoc().canGoAll()) -- { -- 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); -- } -- -- // SouthEast -- if ((nodeE != null) && (nodeS != null)) -- { -- if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) -- { -- addNode(x + 1, y + 1, z, true); -- } -- } -- -- // SouthWest -- if ((nodeS != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) -- { -- addNode(x - 1, y + 1, z, true); -- } -- } -- -- // NorthEast -- if ((nodeN != null) && (nodeE != null)) -- { -- if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) -- { -- addNode(x + 1, y - 1, z, true); -- } -- } -- -- // NorthWest -- if ((nodeN != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) -- { -- addNode(x - 1, y - 1, z, true); -- } -- } -- } -- -- private 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(); -- // reinit node if needed -- if (result.getLoc() != null) -- { -- result.getLoc().set(x, y, z); -- } -- else -- { -- result.setLoc(new NodeLoc(x, y, z)); -- } -- } -- -- return result; -- } -- -- private 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 boolean isHighWeight(int x, int y, int z) -- { -- final CellNode result = getNode(x, y, z); -- if (result == null) -- { -- return true; -- } -- -- if (!result.getLoc().canGoAll()) -- { -- return true; -- } -- if (Math.abs(result.getLoc().getZ() - z) > 16) -- { -- return true; -- } -- -- return false; -- } -- -- private 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (working copy) -@@ -0,0 +1,87 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+ -+/** -+ * @author Hasha -+ */ -+public class Node -+{ -+ // node coords and nswe flag -+ private GeoLocation _loc; -+ -+ // node parent (for reverse path construction) -+ private Node _parent; -+ // node child (for moving over nodes during iteration) -+ private Node _child; -+ -+ // node G cost (movement cost = parent movement cost + current movement cost) -+ private double _cost = -1000; -+ -+ public void setLoc(int x, int y, int z) -+ { -+ _loc = new GeoLocation(x, y, z); -+ } -+ -+ public GeoLocation getLoc() -+ { -+ return _loc; -+ } -+ -+ public void setParent(Node parent) -+ { -+ _parent = parent; -+ } -+ -+ public Node getParent() -+ { -+ return _parent; -+ } -+ -+ public void setChild(Node child) -+ { -+ _child = child; -+ } -+ -+ public Node getChild() -+ { -+ return _child; -+ } -+ -+ public void setCost(double cost) -+ { -+ _cost = cost; -+ } -+ -+ public double getCost() -+ { -+ return _cost; -+ } -+ -+ public void free() -+ { -+ // reset node location -+ _loc = null; -+ -+ // reset node parent, child and cost -+ _parent = null; -+ _child = null; -+ _cost = -1000; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (working copy) -@@ -0,0 +1,306 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import java.util.concurrent.locks.ReentrantLock; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+ -+/** -+ * @author DS, Hasha; Credits to Diamond -+ */ -+public class NodeBuffer -+{ -+ private final ReentrantLock _lock = new ReentrantLock(); -+ private final int _size; -+ private final Node[][] _buffer; -+ -+ // center coordinates -+ private int _cx = 0; -+ private int _cy = 0; -+ -+ // target coordinates -+ private int _gtx = 0; -+ private int _gty = 0; -+ private short _gtz = 0; -+ -+ private Node _current = null; -+ -+ /** -+ * Constructor of NodeBuffer. -+ * @param size : one dimension size of buffer -+ */ -+ public NodeBuffer(int size) -+ { -+ // set size -+ _size = size; -+ -+ // initialize buffer -+ _buffer = new Node[size][size]; -+ for (int x = 0; x < size; x++) -+ { -+ for (int y = 0; y < size; y++) -+ { -+ _buffer[x][y] = 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 Node : first node of path -+ */ -+ public Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) -+ { -+ // set coordinates (middle of the line (gox,goy) - (gtx,gty), will be in the center of the buffer) -+ _cx = gox + ((gtx - gox - _size) / 2); -+ _cy = goy + ((gty - goy - _size) / 2); -+ -+ _gtx = gtx; -+ _gty = gty; -+ _gtz = gtz; -+ -+ _current = getNode(gox, goy, goz); -+ _current.setCost(getCostH(gox, goy, goz)); -+ -+ int count = 0; -+ do -+ { -+ // reached target? -+ if ((_current.getLoc().getGeoX() == _gtx) && (_current.getLoc().getGeoY() == _gty) && (Math.abs(_current.getLoc().getZ() - _gtz) < 8)) -+ { -+ return _current; -+ } -+ -+ // expand current node -+ expand(); -+ -+ // move pointer -+ _current = _current.getChild(); -+ } -+ while ((_current != null) && (count++ < Config.MAX_ITERATIONS)); -+ -+ return null; -+ } -+ -+ public boolean isLocked() -+ { -+ return _lock.tryLock(); -+ } -+ -+ public void free() -+ { -+ _current = null; -+ -+ for (Node[] nodes : _buffer) -+ { -+ for (Node node : nodes) -+ { -+ if (node.getLoc() != null) -+ { -+ node.free(); -+ } -+ } -+ } -+ -+ _lock.unlock(); -+ } -+ -+ /** -+ * Check _current Node and add its neighbors to the buffer. -+ */ -+ private final void expand() -+ { -+ // can't move anywhere, don't expand -+ final byte nswe = _current.getLoc().getNSWE(); -+ if (nswe == 0) -+ { -+ return; -+ } -+ -+ // get geo coords of the node to be expanded -+ final int x = _current.getLoc().getGeoX(); -+ final int y = _current.getLoc().getGeoY(); -+ final short z = (short) _current.getLoc().getZ(); -+ -+ // can move north, expand -+ if ((nswe & GeoStructure.CELL_FLAG_N) != 0) -+ { -+ addNode(x, y - 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move south, expand -+ if ((nswe & GeoStructure.CELL_FLAG_S) != 0) -+ { -+ addNode(x, y + 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_W) != 0) -+ { -+ addNode(x - 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_E) != 0) -+ { -+ addNode(x + 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move north-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NW) != 0) -+ { -+ addNode(x - 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move north-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NE) != 0) -+ { -+ addNode(x + 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SW) != 0) -+ { -+ addNode(x - 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SE) != 0) -+ { -+ addNode(x + 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ } -+ -+ /** -+ * Returns node, if it exists in buffer. -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param z : node Z coord -+ * @return Node : node, if exits in buffer -+ */ -+ private final Node getNode(int x, int y, short z) -+ { -+ // check node X out of coordinates -+ final int ix = x - _cx; -+ if ((ix < 0) || (ix >= _size)) -+ { -+ return null; -+ } -+ -+ // check node Y out of coordinates -+ final int iy = y - _cy; -+ if ((iy < 0) || (iy >= _size)) -+ { -+ return null; -+ } -+ -+ // get node -+ final Node result = _buffer[ix][iy]; -+ -+ // check and update -+ if (result.getLoc() == null) -+ { -+ result.setLoc(x, y, z); -+ } -+ -+ // return node -+ return result; -+ } -+ -+ /** -+ * Add node given by coordinates to the buffer. -+ * @param x : geo X coord -+ * @param y : geo Y coord -+ * @param z : geo Z coord -+ * @param weight : weight of movement to new node -+ */ -+ private final void addNode(int x, int y, short z, int weight) -+ { -+ // get node to be expanded -+ final Node node = getNode(x, y, z); -+ if (node == null) -+ { -+ return; -+ } -+ -+ // Z distance between nearby cells is higher than cell size -+ if (node.getLoc().getZ() > (z + (2 * GeoStructure.CELL_HEIGHT))) -+ { -+ return; -+ } -+ -+ // node was already expanded, return -+ if (node.getCost() >= 0) -+ { -+ return; -+ } -+ -+ node.setParent(_current); -+ if (node.getLoc().getNSWE() != (byte) 0xFF) -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + (weight * Config.OBSTACLE_MULTIPLIER)); -+ } -+ else -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + weight); -+ } -+ -+ Node current = _current; -+ int count = 0; -+ while ((current.getChild() != null) && (count < (Config.MAX_ITERATIONS * 4))) -+ { -+ count++; -+ if (current.getChild().getCost() > node.getCost()) -+ { -+ node.setChild(current.getChild()); -+ break; -+ } -+ current = current.getChild(); -+ } -+ -+ if (count >= (Config.MAX_ITERATIONS * 4)) -+ { -+ System.err.println("Pathfinding: too long loop detected, cost:" + node.getCost()); -+ } -+ -+ current.setChild(node); -+ } -+ -+ /** -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param i : node Z coord -+ * @return double : node cost -+ */ -+ private final double getCostH(int x, int y, int i) -+ { -+ final int dX = x - _gtx; -+ final int dY = y - _gty; -+ final int dZ = (i - _gtz) / GeoStructure.CELL_HEIGHT; -+ -+ // return (Math.abs(dX) + Math.abs(dY) + Math.abs(dZ)) * Config.HEURISTIC_WEIGHT; // Manhattan distance -+ return Math.sqrt((dX * dX) + (dY * dY) + (dZ * dZ)) * Config.HEURISTIC_WEIGHT; // Direct distance -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -- --/** -- * @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 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 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; -- // return super.hashCode(); -- } -- -- @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; -- if (_x != other._x) -- { -- return false; -- } -- if (_y != other._y) -- { -- return false; -- } -- if (_goNorth != other._goNorth) -- { -- return false; -- } -- if (_goEast != other._goEast) -- { -- return false; -- } -- if (_goSouth != other._goSouth) -- { -- return false; -- } -- if (_goWest != other._goWest) -- { -- return false; -- } -- if (_geoHeight != other._geoHeight) -- { -- return false; -- } -- return true; -- } --} -Index: java/org/l2jmobius/gameserver/model/actor/Creature.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/Creature.java (revision 8428) -+++ java/org/l2jmobius/gameserver/model/actor/Creature.java (working copy) -@@ -65,8 +65,6 @@ - import org.l2jmobius.gameserver.enums.TeleportWhereType; - import org.l2jmobius.gameserver.enums.UserInfoType; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; - import org.l2jmobius.gameserver.instancemanager.IdManager; - import org.l2jmobius.gameserver.instancemanager.MapRegionManager; - import org.l2jmobius.gameserver.instancemanager.QuestManager; -@@ -2566,7 +2564,7 @@ - - public boolean disregardingGeodata; - public int onGeodataPathIndex; -- public List geoPath; -+ public List geoPath; - public int geoPathAccurateTx; - public int geoPathAccurateTy; - public int geoPathGtx; -@@ -3443,7 +3441,7 @@ - if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) - { - // Path calculation -- overrides previous movement check -- m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); -+ m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); - if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found - { - if (isPlayer() && !_isFlying && !isInWater) -Index: java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (revision 8424) -+++ java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (working copy) -@@ -12746,7 +12746,7 @@ - { - if (Config.CORRECT_PLAYER_Z) - { -- final int nearestZ = GeoEngine.getInstance().getHigherHeight(getX(), getY(), getZ()); -+ final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); - if (getZ() < nearestZ) - { - teleToLocation(new Location(getX(), getY(), nearestZ)); -Index: java/org/l2jmobius/gameserver/util/GeoUtils.java -=================================================================== ---- java/org/l2jmobius/gameserver/util/GeoUtils.java (revision 8409) -+++ java/org/l2jmobius/gameserver/util/GeoUtils.java (working copy) -@@ -19,7 +19,7 @@ - import java.awt.Color; - - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; - import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; - -@@ -26,25 +26,25 @@ - /** - * @author HorridoJoho - */ --public final class GeoUtils -+public class GeoUtils - { - public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); - - final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); - - while (iter.next()) - { -- final int wx = GeoEngine.getInstance().getWorldX(iter.x()); -- final int wy = GeoEngine.getInstance().getWorldY(iter.y()); -+ final int wx = GeoEngine.getWorldX(iter.x()); -+ final int wy = GeoEngine.getWorldY(iter.y()); - - prim.addPoint(Color.RED, wx, wy, z); - } -@@ -53,21 +53,21 @@ - - public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); -- int wy = GeoEngine.getInstance().getWorldY(prevY); -+ int wx = GeoEngine.getWorldX(prevX); -+ int wy = GeoEngine.getWorldY(prevY); - int wz = iter.z(); - prim.addPoint(Color.RED, wx, wy, wz); - -@@ -78,8 +78,8 @@ - - if ((curX != prevX) || (curY != prevY)) - { -- wx = GeoEngine.getInstance().getWorldX(curX); -- wy = GeoEngine.getInstance().getWorldY(curY); -+ wx = GeoEngine.getWorldX(curX); -+ wy = GeoEngine.getWorldY(curY); - wz = iter.z(); - - prim.addPoint(Color.RED, wx, wy, wz); -@@ -93,7 +93,7 @@ - - private static Color getDirectionColor(int x, int y, int z, int nswe) - { -- if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) -+ if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) - { - return Color.GREEN; - } -@@ -109,9 +109,8 @@ - int iPacket = 0; - - ExServerPrimitive exsp = null; -- final GeoEngine ge = GeoEngine.getInstance(); -- final int playerGx = ge.getGeoX(player.getX()); -- final int playerGy = ge.getGeoY(player.getY()); -+ final int playerGx = GeoEngine.getGeoX(player.getX()); -+ final int playerGy = GeoEngine.getGeoY(player.getY()); - for (int dx = -geoRadius; dx <= geoRadius; ++dx) - { - for (int dy = -geoRadius; dy <= geoRadius; ++dy) -@@ -135,12 +134,12 @@ - final int gx = playerGx + dx; - final int gy = playerGy + dy; - -- final int x = ge.getWorldX(gx); -- final int y = ge.getWorldY(gy); -- final int z = ge.getNearestZ(gx, gy, player.getZ()); -+ final int x = GeoEngine.getWorldX(gx); -+ final int y = GeoEngine.getWorldY(gy); -+ final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); - - // north arrow -- Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); -+ Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); - 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); -@@ -147,7 +146,7 @@ - exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z); - - // east arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); - 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); -@@ -154,13 +153,13 @@ - exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z); - - // south arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); - 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, Cell.NSWE_WEST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); - 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); -@@ -188,15 +187,15 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_EAST; -+ return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_EAST; -+ return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; - } - else - { -- return Cell.NSWE_EAST; -+ return GeoStructure.CELL_FLAG_E; // Direction.EAST; - } - } - else if (x < lastX) // west -@@ -203,26 +202,27 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_WEST; -+ return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_WEST; -+ return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; - } - else - { -- return Cell.NSWE_WEST; -+ return GeoStructure.CELL_FLAG_W; // Direction.WEST; - } - } -- else // unchanged x -+ else -+ // unchanged x - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH; -+ return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH; -+ return GeoStructure.CELL_FLAG_N; // Direction.NORTH; - } - else - { -Index: java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java -=================================================================== ---- java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (nonexistent) -+++ java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (working copy) -@@ -0,0 +1,386 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.tools.geodataconverter; -+ -+import java.io.BufferedOutputStream; -+import java.io.File; -+import java.io.FileOutputStream; -+import java.io.RandomAccessFile; -+import java.nio.ByteOrder; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.Scanner; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.commons.util.PropertiesParser; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoDataConverter -+{ -+ private static GeoFormat _format; -+ private static ABlock[][] _blocks; -+ -+ public static void main(String[] args) -+ { -+ // initialize config -+ loadGeoengineConfigs(); -+ -+ // get geodata format -+ String type = ""; -+ try (Scanner scn = new Scanner(System.in)) -+ { -+ while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) -+ { -+ System.out.println("GeoDataConverter: Select source geodata type:"); -+ System.out.println(" J: L2J (e.g. 23_22.l2j)"); -+ System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); -+ System.out.println(" E: Exit"); -+ System.out.print("Choice: "); -+ type = scn.next(); -+ } -+ } -+ if (type.equalsIgnoreCase("E")) -+ { -+ System.exit(0); -+ } -+ -+ _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; -+ -+ // start conversion -+ System.out.println("GeoDataConverter: Converting all " + _format + " files."); -+ -+ // initialize geodata container -+ _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files -+ int converted = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) -+ { -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) -+ { -+ final String input = String.format(_format.getFilename(), rx, ry); -+ final String filepath = Config.GEODATA_PATH; -+ final File f = new File(filepath + input); -+ if (f.exists() && !f.isDirectory()) -+ { -+ // load geodata -+ if (!loadGeoBlocks(input)) -+ { -+ System.out.println("GeoDataConverter: Unable to load " + input + " region file."); -+ continue; -+ } -+ -+ // recalculate nswe -+ if (!recalculateNswe()) -+ { -+ System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); -+ continue; -+ } -+ -+ // save geodata -+ final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); -+ if (!saveGeoBlocks(output)) -+ { -+ System.out.println("GeoDataConverter: Unable to save " + output + " region file."); -+ continue; -+ } -+ -+ converted++; -+ System.out.println("GeoDataConverter: Created " + output + " region file."); -+ } -+ } -+ } -+ System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); -+ } -+ -+ /** -+ * Loads geo blocks from buffer of the region file. -+ * @param filename : The name of the to load. -+ * @return boolean : True when successful. -+ */ -+ private static boolean loadGeoBlocks(String filename) -+ { -+ // region file is load-able, try to load it -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) -+ { -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) -+ if (_format == GeoFormat.L2OFF) -+ { -+ for (int i = 0; i < 18; i++) -+ { -+ buffer.get(); -+ } -+ } -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ if (_format == GeoFormat.L2J) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2J: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2J: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ else -+ { -+ // get block type -+ final short type = buffer.getShort(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ default: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ if (buffer.remaining() > 0) -+ { -+ System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ return false; -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); -+ return false; -+ } -+ } -+ -+ /** -+ * Recalculate diagonal flags for the region file. -+ * @return boolean : True when successful. -+ */ -+ private static boolean recalculateNswe() -+ { -+ try -+ { -+ for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) -+ { -+ for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) -+ { -+ // get block -+ final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // skip flat blocks -+ if (block instanceof BlockFlat) -+ { -+ continue; -+ } -+ -+ // for complex and multilayer blocks go though all layers -+ short height = Short.MAX_VALUE; -+ int index; -+ while ((index = block.getIndexBelow(x, y, height)) != -1) -+ { -+ // get height and nswe -+ height = block.getHeight(index); -+ byte nswe = block.getNswe(index); -+ -+ // update nswe with diagonal flags -+ nswe = updateNsweBelow(x, y, height, nswe); -+ -+ // set nswe of the cell -+ block.setNswe(index, nswe); -+ } -+ } -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ /** -+ * Updates the NSWE flag with diagonal flags. -+ * @param x : Geodata X coordinate. -+ * @param y : Geodata Y coordinate. -+ * @param z : Geodata Z coordinate. -+ * @param nsweValue : NSWE flag to be updated. -+ * @return byte : Updated NSWE flag. -+ */ -+ private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) -+ { -+ byte nswe = nsweValue; -+ -+ // calculate virtual layer height -+ final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); -+ -+ // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) -+ final byte nsweN = getNsweBelow(x, y - 1, height); -+ final byte nsweS = getNsweBelow(x, y + 1, height); -+ final byte nsweW = getNsweBelow(x - 1, y, height); -+ final byte nsweE = getNsweBelow(x + 1, y, height); -+ -+ // north-west -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NW; -+ } -+ -+ // north-east -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NE; -+ } -+ -+ // south-west -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SW; -+ } -+ -+ // south-east -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SE; -+ } -+ -+ return nswe; -+ } -+ -+ private static byte getNsweBelow(int geoX, int geoY, short worldZ) -+ { -+ // out of geo coordinates -+ if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) -+ { -+ return 0; -+ } -+ -+ // out of geo coordinates -+ if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) -+ { -+ return 0; -+ } -+ -+ // get block -+ final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // get index, when valid, return nswe -+ final int index = block.getIndexBelow(geoX, geoY, worldZ); -+ return index == -1 ? 0 : block.getNswe(index); -+ } -+ -+ /** -+ * Save region file to file. -+ * @param filename : The name of file to save. -+ * @return boolean : True when successful. -+ */ -+ private static boolean saveGeoBlocks(String filename) -+ { -+ try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) -+ { -+ // loop over region blocks and save each block -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[ix][iy].saveBlock(bos); -+ } -+ } -+ -+ // flush data to file -+ bos.flush(); -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ private static void loadGeoengineConfigs() -+ { -+ final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); -+ Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); -+ Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); -+ Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); -+ Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); -+ Config.PATHFINDING = geoData.getBoolean("PathFinding", true); -+ Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -+ Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); -+ Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); -+ Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); -+ Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); -+ Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); -+ } -+} -\ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/geodata/Readme.txt b/L2J_Mobius_6.0_Fafurion/dist/game/data/geodata/Readme.txt index a480c8c380..2611882e56 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/geodata/Readme.txt +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/geodata/Readme.txt @@ -23,8 +23,8 @@ a - Prerequisites b - Make it work ---------------------------------------------- -To make geodata working: -* unpack your geodata files into "/data/geodata" folder -* open "/config/GeoEngine.ini" with your favorite text editor and then edit following config: - - CoordSynchronize = 2 -* If you do not use any geodata files, the server will automatically change this setting to -1. +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. diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java index 60cfac5c02..63f6ac589c 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java @@ -295,7 +295,7 @@ public class FourSepulchers extends AbstractNpcAI implements IXmlReader { if ((npc != null) && !npc.isDead()) { - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), npc.getSpawn().getLocation().getX() + getRandom(-400, 400), npc.getSpawn().getLocation().getY() + getRandom(-400, 400), npc.getZ(), npc.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), npc.getSpawn().getLocation().getX() + getRandom(-400, 400), npc.getSpawn().getLocation().getY() + getRandom(-400, 400), npc.getZ(), npc.getInstanceWorld()); if (Util.calculateDistance(npc, npc.getSpawn().getLocation(), false, false) < 600) { npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java index 89f40f2ad4..0d925415a5 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java @@ -270,7 +270,7 @@ public class PrimevalIsle extends AbstractNpcAI final double cos = Math.cos(radian); final int newX = (int) (npc.getX() + (cos * distance)); final int newY = (int) (npc.getY() + (sin * distance)); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, npc.getZ(), npc.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, npc.getZ(), npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, loc, 0); } else if (ag_type == 1) diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java index 0a77f7404d..772f5fb5d7 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java @@ -78,7 +78,7 @@ public class BoyAndGirl extends AbstractNpcAI startQuestTimer("NPC_SHOUT", 10000 + (getRandom(5) * 1000), npc, null); npc.setRunning(); final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 200, 600); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); return super.onSpawn(npc); } @@ -86,7 +86,7 @@ public class BoyAndGirl extends AbstractNpcAI public void onMoveFinished(Npc npc) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 200, 600); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); super.onMoveFinished(npc); } diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java index 558f2631e6..3bcf3810cb 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java @@ -48,7 +48,7 @@ public class Devno extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java index 7525cc99e8..ce81f5c05c 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java @@ -48,7 +48,7 @@ public class Eleve extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java index 7d9150b156..f4e942c326 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java @@ -46,7 +46,7 @@ public class Handermonkey extends AbstractNpcAI { final int x = npc.getSpawn().getX() + (getRandom(-100, 100)); final int y = npc.getSpawn().getY() + (getRandom(-100, 100)); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x, y, npc.getZ(), npc.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x, y, npc.getZ(), npc.getInstanceWorld()); addMoveToDesire(npc, loc, 0); } else diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java index c993ce8936..7df61da041 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java @@ -48,7 +48,7 @@ public class Karonf extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java index 6e8cbee920..589d376f6b 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java @@ -48,7 +48,7 @@ public class Marsha extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java index c616abccc6..5c09d531c8 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java @@ -48,7 +48,7 @@ public class Morgan extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java index 3652b4ab02..fe20952121 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java @@ -48,7 +48,7 @@ public class Rubentis extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", 10000 + (getRandom(5) * 1000), npc, null); } diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java index beece64521..b0944f763d 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java @@ -63,7 +63,7 @@ public class Vortex extends AbstractNpcAI final int x = (int) (attackers.getX() + (600 * Math.cos(radians))); final int y = (int) (attackers.getY() + (600 * Math.sin(radians))); final int z = attackers.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(attackers.getX(), attackers.getY(), attackers.getZ(), x, y, z, attackers.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(attackers.getX(), attackers.getY(), attackers.getZ(), x, y, z, attackers.getInstanceWorld()); attackers.broadcastPacket(new FlyToLocation(attackers, x, y, z, FlyToLocation.FlyType.THROW_UP, 800, 800, 800)); attackers.setXYZ(loc); attackers.broadcastPacket(new ValidateLocation(attackers)); diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/others/FleeMonsters.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/others/FleeMonsters.java index 9346aebc95..b5de804b28 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/others/FleeMonsters.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/ai/others/FleeMonsters.java @@ -68,7 +68,7 @@ public class FleeMonsters extends AbstractNpcAI final int posX = (int) (npc.getX() + (FLEE_DISTANCE * Math.cos(radians))); final int posY = (int) (npc.getY() + (FLEE_DISTANCE * Math.sin(radians))); final int posZ = npc.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, attacker.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); return super.onAttack(npc, attacker, damage, isSummon); } diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index 524652cbc1..18511c6541 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { @@ -133,8 +133,8 @@ public class AdminGeodata implements IAdminCommandHandler } case "admin_geomap": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y + " (" + ((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + "," + ((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + " to " + ((((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + "," + ((((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + ")"); break; } diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java index f3dffa1227..76572f7f25 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java @@ -57,8 +57,8 @@ public class AdminMissingHtmls implements IAdminCommandHandler { case "admin_geomap_missing_htmls": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final int topLeftX = (x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE; final int topLeftY = (y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE; final int bottomRightX = (((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1; diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index fdb2319138..a0a34f1051 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -19,9 +19,9 @@ package handlers.admincommandhandlers; import java.util.List; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; +import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.util.BuilderUtil; @@ -44,13 +44,13 @@ public class AdminPathNode implements IAdminCommandHandler } if (activeChar.getTarget() != null) { - final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { BuilderUtil.sendSysMessage(activeChar, "No Route!"); return true; } - for (AbstractNodeLoc a : path) + for (Location a : path) { BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/Blink.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/Blink.java index 22f2c84eae..22697e8e20 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/Blink.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/Blink.java @@ -88,7 +88,7 @@ public class Blink extends AbstractEffect final int y = effected.getY() + y1; final int z = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, destination, _flyType, _flySpeed, _flyDelay, _animationSpeed)); diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/Fear.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/Fear.java index 95a5afdf98..88c795e04b 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/Fear.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/Fear.java @@ -100,7 +100,7 @@ public class Fear extends AbstractEffect final int posY = (int) (effected.getY() + (FEAR_RANGE * Math.sin(radians))); final int posZ = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); } } diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java index fddd94c60a..55c2e78d8a 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java @@ -57,7 +57,7 @@ public class FlyAway extends AbstractEffect final int y = (int) (effector.getY() - (nRadius * (dy / distance))); final int z = effector.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.broadcastPacket(new FlyToLocation(effected, destination, FlyType.THROW_UP)); effected.setXYZ(destination); diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java index 3c3f9aca62..14809ac499 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java @@ -179,7 +179,7 @@ public class KnockBack extends AbstractEffect final int x = (int) (effected.getX() + (_distance * Math.cos(radians))); final int y = (int) (effected.getY() + (_distance * Math.sin(radians))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, loc, _type, _speed, _delay, _animationSpeed)); diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/PullBack.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/PullBack.java index ed6a37eff6..38fe2fe9d0 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/PullBack.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/PullBack.java @@ -73,7 +73,7 @@ public class PullBack extends AbstractEffect } // In retail, you get debuff, but you are not even moved if there is obstacle. You are still disabled from using skills and moving though. - if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effector.getInstanceWorld())) + if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effected.getInstanceWorld())) { effected.broadcastPacket(new FlyToLocation(effected, effector, _type, _speed, _delay, _animationSpeed)); effected.setXYZ(effector.getX(), effector.getY(), GeoEngine.getInstance().getHeight(effector.getX(), effector.getY(), effector.getZ()) + 10); diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java index d6f9664141..813e496c47 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java @@ -87,7 +87,7 @@ public class TeleportToSummon extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = summon.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java index d3c263978c..c9dc34ac22 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java @@ -76,7 +76,7 @@ public class TeleportToTarget extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java index f2148adb54..c7ca6f64cb 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java @@ -65,7 +65,7 @@ public class RollingDice implements IItemHandler final int x = player.getX() + x1; final int y = player.getY() + y1; final int z = player.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); Broadcast.toSelfAndKnownPlayers(player, new Dice(player.getObjectId(), itemId, number, destination.getX(), destination.getY(), destination.getZ())); final SystemMessage sm = new SystemMessage(SystemMessageId.C1_HAS_ROLLED_A_S2); diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/targethandlers/Ground.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/targethandlers/Ground.java index acce8f1b2f..1328ff01b5 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/targethandlers/Ground.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/targethandlers/Ground.java @@ -52,7 +52,7 @@ public class Ground implements ITargetTypeHandler return null; } - if (!GeoEngine.getInstance().canSeeTarget(creature, worldPosition)) + if (!GeoEngine.getInstance().canSeeLocation(creature, worldPosition)) { if (sendMessage) { diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/Config.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/Config.java index b187261fde..b0bf7da550 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/Config.java @@ -61,6 +61,7 @@ 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; @@ -956,12 +957,17 @@ public class Config // GeoEngine // -------------------------------------------------- public static Path GEODATA_PATH; + public static GeoType GEODATA_TYPE; public static boolean PATHFINDING; public static String PATHFIND_BUFFERS; - public static float LOW_WEIGHT; - public static float MEDIUM_WEIGHT; - public static float HIGH_WEIGHT; - public static float DIAGONAL_WEIGHT; + public static int MOVE_WEIGHT; + public static int MOVE_WEIGHT_DIAG; + public static int OBSTACLE_WEIGHT; + 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; /** Attribute System */ public static int S_WEAPON_STONE; @@ -2543,12 +2549,17 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); + GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); - MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); - HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); - DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); + MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); + OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); + HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); + MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); + MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); // Load AllowedPlayerRaces config file (if exists) final PropertiesParser AllowedPlayerRaces = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE); diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/commons/util/CommonUtil.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/commons/util/CommonUtil.java index 14ae3a4560..ab7837d827 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/commons/util/CommonUtil.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/commons/util/CommonUtil.java @@ -584,4 +584,15 @@ public class CommonUtil final DecimalFormat formatter = new DecimalFormat(format, new DecimalFormatSymbols(Locale.ENGLISH)); return formatter.format(value); } + + /** + * @param numToTest : The number to test. + * @param min : The minimum limit. + * @param max : The maximum limit. + * @return the number or one of the limit (mininum / maximum). + */ + public static int limit(int numToTest, int min, int max) + { + return (numToTest > max) ? max : ((numToTest < min) ? min : numToTest); + } } diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/commons/util/Point2D.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/commons/util/Point2D.java new file mode 100644 index 0000000000..ebb6272e5e --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/commons/util/Point2D.java @@ -0,0 +1,180 @@ +/* + * 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 . + */ +package org.l2jmobius.commons.util; + +import java.util.Objects; + +/** + * A datatype used to retain a 2D (x/y) point. It got the capability to be set and cleaned. + */ +public class Point2D +{ + protected volatile int _x; + protected volatile int _y; + + public Point2D(int x, int y) + { + _x = x; + _y = y; + } + + @Override + public Point2D clone() + { + return new Point2D(_x, _y); + } + + @Override + public String toString() + { + return _x + ", " + _y; + } + + @Override + public int hashCode() + { + return Objects.hash(_x, _y); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final Point2D other = (Point2D) obj; + return (_x == other._x) && (_y == other._y); + } + + /** + * @param x : The X coord to test. + * @param y : The Y coord to test. + * @return True if all coordinates equals this {@link Point2D} coordinates. + */ + public boolean equals(int x, int y) + { + return (_x == x) && (_y == y); + } + + public int getX() + { + return _x; + } + + public void setX(int x) + { + _x = x; + } + + public int getY() + { + return _y; + } + + public void setY(int y) + { + _y = y; + } + + public void set(int x, int y) + { + _x = x; + _y = y; + } + + /** + * Refresh the current {@link Point2D} using a reference {@link Point2D} and a distance. The new destination is calculated to go in opposite side of the {@link Point2D} reference.
+ *
+ * This method is perfect to calculate fleeing characters position. + * @param referenceLoc : The Point2D used as position. + * @param distance : The distance to be set between current and new position. + */ + public void setFleeing(Point2D referenceLoc, int distance) + { + final double xDiff = referenceLoc.getX() - _x; + final double yDiff = referenceLoc.getY() - _y; + + final double yxRation = Math.abs(xDiff / yDiff); + + final int y = (int) (distance / (yxRation + 1)); + final int x = (int) (y * yxRation); + + _x += (xDiff < 0 ? x : -x); + _y += (yDiff < 0 ? y : -y); + } + + public void clean() + { + _x = 0; + _y = 0; + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @return The distance between this {@Point2D} and some given coordinates. + */ + public double distance2D(int x, int y) + { + final double dx = (double) _x - x; + final double dy = (double) _y - y; + + return Math.sqrt((dx * dx) + (dy * dy)); + } + + /** + * @param point : The {@link Point2D} to test. + * @return The distance between this {@Point2D} and the {@link Point2D} set as parameter. + */ + public double distance2D(Point2D point) + { + return distance2D(point.getX(), point.getY()); + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of some given coordinates. + */ + public boolean isIn2DRadius(int x, int y, int radius) + { + return distance2D(x, y) < radius; + } + + /** + * @param point : The Point2D to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of the {@link Point2D} set as parameter. + */ + public boolean isIn2DRadius(Point2D point, int radius) + { + return distance2D(point) < radius; + } +} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/AttackableAI.java index 547963e3e8..dc29d4cf6e 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/AttackableAI.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/ai/AttackableAI.java @@ -578,7 +578,7 @@ public class AttackableAI extends CreatureAI } // Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast) - final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); + final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); moveTo(moveLoc.getX(), moveLoc.getY(), moveLoc.getZ()); } } @@ -801,7 +801,7 @@ public class AttackableAI extends CreatureAI final int newZ = npc.getZ() + 30; // Mobius: Verify destination. Prevents wall collision issues and fixes monsters not avoiding obstacles. - moveTo(GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); + moveTo(GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); } return; } diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/data/SpawnTable.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/data/SpawnTable.java index a81201f69e..19f0db5b3e 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/data/SpawnTable.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/data/SpawnTable.java @@ -114,8 +114,8 @@ public class SpawnTable } // XML file for spawn - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); // Write info to XML @@ -192,8 +192,8 @@ public class SpawnTable if (update) { - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = spawn.getNpcSpawnTemplate() != null ? spawn.getNpcSpawnTemplate().getSpawnTemplate().getFile() : new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); final File tempFile = new File(spawnFile.getAbsolutePath().substring(Config.DATAPACK_ROOT.getAbsolutePath().length() + 1).replace('\\', '/') + ".tmp"); try diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/enums/GeoType.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/enums/GeoType.java new file mode 100644 index 0000000000..f77aa5eac4 --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/enums/GeoType.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.enums; + +public enum GeoType +{ + L2J("%d_%d.l2j"), + L2OFF("%d_%d_conv.dat"); + + private final String _filename; + + private GeoType(String filename) + { + _filename = filename; + } + + public String getFilename() + { + return _filename; + } +} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java new file mode 100644 index 0000000000..e62f50a8df --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java @@ -0,0 +1,144 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 9d255a5f54..7bef9c770b 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,72 +16,63 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.channels.FileChannel.MapMode; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.logging.Level; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; import java.util.logging.Logger; import org.l2jmobius.Config; +import org.l2jmobius.commons.util.CommonUtil; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; -import org.l2jmobius.gameserver.geoengine.geodata.IRegion; -import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; -import org.l2jmobius.gameserver.geoengine.geodata.Region; +import org.l2jmobius.gameserver.enums.GeoType; +import org.l2jmobius.gameserver.enums.MoveDirectionType; +import org.l2jmobius.gameserver.geoengine.geodata.ABlock; +import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; +import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; +import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; +import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.model.interfaces.ILocational; -import org.l2jmobius.gameserver.util.GeoUtils; -import org.l2jmobius.gameserver.util.LinePointIterator; -import org.l2jmobius.gameserver.util.LinePointIterator3D; -/** - * @author -Nemesiss-, HorridoJoho - */ public class GeoEngine { - private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - - private static final int WORLD_MIN_X = -655360; - private static final int WORLD_MIN_Y = -589824; - private static final int WORLD_MIN_Z = -16384; - - /** 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; - - /** The regions array */ - private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; - private static final int ELEVATED_SEE_OVER_DISTANCE = 2; - private static final int MAX_SEE_OVER_HEIGHT = 48; - protected GeoEngine() + private final ABlock[][] _blocks; + private final BlockNull _nullBlock; + + // Pre-allocated buffers. + private final BufferHolder[] _buffers; + + public GeoEngine() { LOGGER.info("GeoEngine: Initializing..."); - for (int i = 0; i < _regions.length(); i++) - { - _regions.set(i, NullRegion.INSTANCE); - } + // Initialize block container. + _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; + + // Load null block. + _nullBlock = new BlockNull(); + + // Initialize multilayer temporarily buffer. + BlockMultilayer.initialize(); + + // Load geo files according to geoengine config setup. int loaded = 0; try { @@ -92,23 +83,52 @@ public class GeoEngine final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); if (Files.exists(geoFilePath)) { - try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + // Region file is load-able, try to load it. + if (loadGeoBlocks(regionX, regionY)) { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); loaded++; } } + else + { + // Region file is not load-able, load null blocks. + loadNullBlocks(regionX, regionY); + } } } } catch (Exception e) { - LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + LOGGER.warning("GeoEngine: Failed to load geodata! " + e); System.exit(1); } - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + // Release multilayer block temporarily buffer. + BlockMultilayer.release(); + + String[] array = Config.PATHFIND_BUFFERS.split(";"); + _buffers = new BufferHolder[array.length]; + + int count = 0; + for (int i = 0; i < array.length; i++) + { + String buf = array[i]; + String[] args = buf.split("x"); + + try + { + int size = Integer.parseInt(args[1]); + count += size; + _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); + } + catch (Exception e) + { + LOGGER.warning("Could not load buffer setting:" + buf + ". " + e); + } + } + LOGGER.info("Loaded " + count + " node buffers."); + // Avoid wrong configs when no files are loaded. if ((loaded == 0) && Config.PATHFINDING) { @@ -118,616 +138,913 @@ public class GeoEngine } /** - * @param geoX - * @param geoY - * @return the region + * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer and log this situation. + * @param size : pre-calculated minimal required size + * @return NodeBuffer : buffer */ - private IRegion getRegion(int geoX, int geoY) + private NodeBuffer getBuffer(int size) { - final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); - if ((region < 0) || (region >= _regions.length())) + NodeBuffer current = null; + for (BufferHolder holder : _buffers) { - return null; - } - return _regions.get(region); - } - - /** - * @param filePath - * @param regionX - * @param regionY - * @throws IOException - */ - 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()).load().order(ByteOrder.LITTLE_ENDIAN))); - } - } - - /** - * @param regionX - * @param regionY - */ - public void unloadRegion(int regionX, int regionY) - { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); - } - - /** - * @param geoX - * @param geoY - * @return if geodata exist - */ - public boolean hasGeoPos(int geoX, int geoY) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return false; - } - return region.hasGeo(); - } - - /** - * Checks the specified position for available geodata. - * @param x the world x - * @param y the world y - * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise - */ - public boolean hasGeo(int x, int y) - { - return hasGeoPos(getGeoX(x), getGeoY(y)); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe check - */ - public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return true; - } - return region.checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe anti-corner cut - */ - public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) - { - boolean can = true; - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - return can && checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the nearest Z value - */ - public int getNearestZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNearestZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next lower Z value - */ - public int getNextLowerZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextLowerZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next higher Z value - */ - public int getNextHigherZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextHigherZ(geoX, geoY, worldZ); - } - - /** - * Gets the Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHeight(int x, int y, int z) - { - return getNearestZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next lower Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getLowerHeight(int x, int y, int z) - { - return getNextLowerZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next higher Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHigherHeight(int x, int y, int z) - { - return getNextHigherZ(getGeoX(x), getGeoY(y), z); - } - - /** - * @param worldX - * @return the geo X - */ - public int getGeoX(int worldX) - { - return (worldX - WORLD_MIN_X) / 16; - } - - /** - * @param worldY - * @return the geo Y - */ - public int getGeoY(int worldY) - { - return (worldY - WORLD_MIN_Y) / 16; - } - - /** - * @param worldZ - * @return the geo Z - */ - public int getGeoZ(int worldZ) - { - return (worldZ - WORLD_MIN_Z) / 16; - } - - /** - * @param geoX - * @return the world X - */ - public int getWorldX(int geoX) - { - return (geoX * 16) + WORLD_MIN_X + 8; - } - - /** - * @param geoY - * @return the world Y - */ - public int getWorldY(int geoY) - { - return (geoY * 16) + WORLD_MIN_Y + 8; - } - - /** - * @param geoZ - * @return the world Z - */ - public int getWorldZ(int geoZ) - { - return (geoZ * 16) + WORLD_MIN_Z + 8; - } - - /** - * Can see target. Doors as target always return true. Checks doors between. - * @param cha the character - * @param target the target - * @return {@code true} if the character can see the target (LOS), {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, WorldObject target) - { - if (target.isDoor()) - { - // Can always see doors. - return true; - } - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); - } - - /** - * Can see target. Checks doors between. - * @param cha the character - * @param worldPosition the world position - * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) - { - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param world - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @param tworld the target's instanceId - * @return - */ - public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) - { - return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instance - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) - { - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) - { - return false; - } - return canSeeTarget(x, y, z, tx, ty, tz); - } - - /** - * @param prevX - * @param prevY - * @param prevGeoZ - * @param curX - * @param curY - * @param nswe - * @return the LOS Z value - */ - private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) - { - if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) - { - throw new RuntimeException("Multiple directions!"); - } - - if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) - { - return getNearestZ(curX, curY, prevGeoZ); - } - - return getNextHigherZ(curX, curY, prevGeoZ); - } - - /** - * Can see target. Does not check doors between. - * @param xValue the x coordinate - * @param yValue the y coordinate - * @param zValue the z coordinate - * @param txValue the target's x coordinate - * @param tyValue the target's y coordinate - * @param tzValue the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) - { - int x = xValue; - int y = yValue; - int tx = txValue; - int ty = tyValue; - - int geoX = getGeoX(x); - int geoY = getGeoY(y); - int tGeoX = getGeoX(tx); - int tGeoY = getGeoY(ty); - - int z = getNearestZ(geoX, geoY, zValue); - int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - // Fastpath. - if ((geoX == tGeoX) && (geoY == tGeoY)) - { - if (hasGeoPos(tGeoX, tGeoY)) - { - return z == tz; - } - return true; - } - - if (tz > z) - { - int tmp = tx; - tx = x; - x = tmp; - - tmp = ty; - ty = y; - y = tmp; - - tmp = tz; - tz = z; - z = tmp; - - tmp = tGeoX; - tGeoX = geoX; - geoX = tmp; - - tmp = tGeoY; - tGeoY = geoY; - geoY = tmp; - } - - final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); - // First point is guaranteed to be available, skip it, we can always see our own position. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - final int prevZ = pointIter.z(); - int prevGeoZ = prevZ; - int ptIndex = 0; - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - - if ((curX == prevX) && (curY == prevY)) + // Find proper size of buffer. + if (holder._size < size) { continue; } - final int beeCurZ = pointIter.z(); - int curGeoZ = prevGeoZ; - - // Check if the position has geodata. - if (hasGeoPos(curX, curY)) + // Find unlocked NodeBuffer. + for (NodeBuffer buffer : holder._buffer) { - final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); - curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); - int maxHeight; - if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) + if (!buffer.isLocked()) { - maxHeight = z + MAX_SEE_OVER_HEIGHT; - } - else - { - maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; + continue; } - boolean canSeeThrough = false; - if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) + return buffer; + } + + LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); + + // NodeBuffer not found, allocate temporary buffer. + current = new NodeBuffer(holder._size); + current.isLocked(); + } + + return current; + } + + /** + * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + * @return boolean : True, when geodata file was loaded without problem. + */ + private boolean loadGeoBlocks(int regionX, int regionY) + { + final String filename = String.format(Config.GEODATA_TYPE.getFilename(), regionX, regionY); + final String filepath = Config.GEODATA_PATH + "\\" + filename; + + // Standard load. + try (RandomAccessFile raf = new RandomAccessFile(filepath, "r"); + FileChannel fc = raf.getChannel()) + { + // Initialize file buffer. + MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // Load 18B header for L2OFF geodata (1st and 2nd byte...region X and Y). + if (Config.GEODATA_TYPE == GeoType.L2OFF) + { + for (int i = 0; i < 18; i++) { - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) + buffer.get(); + } + } + + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Loop over region blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + if (Config.GEODATA_TYPE == GeoType.L2J) { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) - { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); + // Get block type. + final byte type = buffer.get(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + case GeoStructure.TYPE_MULTILAYER_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + default: + { + throw new IllegalArgumentException("Unknown block type: " + type); + } + } } else { - canSeeThrough = true; + // Get block type. + final short type = buffer.getShort(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + default: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + } } } + } + + // Check data consistency. + if (buffer.remaining() > 0) + { + LOGGER.warning("Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); + } + + // Loading was successful. + return true; + } + catch (Exception e) + { + // An error occured while loading, load null blocks. + LOGGER.warning("Error loading " + filename + " region file. " + e); + + // Replace whole region file with null blocks. + loadNullBlocks(regionX, regionY); + + // Loading was not successful. + return false; + } + } + + /** + * Loads null blocks. Used when no region file is detected or an error occurs during loading. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + */ + private void loadNullBlocks(int regionX, int regionY) + { + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Load all null blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + _blocks[blockX + ix][blockY + iy] = _nullBlock; + } + } + } + + /** + * Converts world X to geodata X. + * @param worldX + * @return int : Geo X + */ + public static int getGeoX(int worldX) + { + return (worldX - World.WORLD_X_MIN) >> 4; + } + + /** + * Converts world Y to geodata Y. + * @param worldY + * @return int : Geo Y + */ + public static int getGeoY(int worldY) + { + return (worldY - World.WORLD_Y_MIN) >> 4; + } + + /** + * Converts geodata X to world X. + * @param geoX + * @return int : World X + */ + public static int getWorldX(int geoX) + { + return (geoX << 4) + World.WORLD_X_MIN + 8; + } + + /** + * Converts geodata Y to world Y. + * @param geoY + * @return int : World Y + */ + public static int getWorldY(int geoY) + { + return (geoY << 4) + World.WORLD_Y_MIN + 8; + } + + /** + * Returns block of geodata on given coordinates. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return {@link ABlock} : Block of geodata. + */ + public ABlock getBlock(int geoX, int geoY) + { + final int x = geoX / GeoStructure.BLOCK_CELLS_X; + if ((x < 0) || (x >= GeoStructure.GEO_BLOCKS_X)) + { + return null; + } + final int y = geoY / GeoStructure.BLOCK_CELLS_Y; + if ((y < 0) || (y >= GeoStructure.GEO_BLOCKS_Y)) + { + return null; + } + return _blocks[x][y]; + } + + /** + * Check if geo coordinates has geo. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return boolean : True, if given geo coordinates have geodata + */ + public boolean hasGeoPos(int geoX, int geoY) + { + final ABlock block = getBlock(geoX, geoY); + return (block != null) && block.hasGeoPos(); + } + + /** + * Returns the height of cell, which is closest to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell geodata Z coordinate, closest to given coordinates. + */ + public short getHeightNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return (short) worldZ; + } + return block.getHeightNearest(geoX, geoY, worldZ); + } + + /** + * Returns the NSWE flag byte of cell, which is closes to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. + */ + public byte getNsweNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return GeoStructure.CELL_FLAG_ALL; + } + return block.getNsweNearest(geoX, geoY, worldZ); + } + + /** + * Check if world coordinates has geo. + * @param worldX : World X + * @param worldY : World Y + * @return boolean : True, if given world coordinates have geodata + */ + public boolean hasGeo(int worldX, int worldY) + { + return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param loc : The location used as reference. + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(Location loc) + { + return getHeightNearest(getGeoX(loc.getX()), getGeoY(loc.getY()), loc.getZ()); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param worldX : world x + * @param worldY : world y + * @param worldZ : world z + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(int worldX, int worldY, int worldZ) + { + return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); + } + + /** + * Check line of sight from {@link WorldObject} to {@link WorldObject}.
+ * @param object : The origin object. + * @param target : The target object. + * @return True, when object can see target. + */ + public boolean canSeeTarget(WorldObject object, WorldObject target) + { + // Can always see doors. + if (target.isDoor()) + { + return true; + } + + if (object.getInstanceWorld() != target.getInstanceWorld()) + { + return false; + } + + if (DoorData.getInstance().checkIfDoorsBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld())) + { + return false; + } + + // Get object's and target's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + double theight = 0; + if (target instanceof Creature) + { + theight += (((Creature) target).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + return canSee(object.getX(), object.getY(), object.getZ(), oheight, target.getX(), target.getY(), target.getZ(), theight) && canSee(target.getX(), target.getY(), target.getZ(), theight, object.getX(), object.getY(), object.getZ(), oheight); + } + + /** + * Check line of sight from {@link WorldObject} to {@link Location}.
+ * Note: The check uses {@link Location}'s real Z coordinate (e.g. point above ground), not its geodata representation. + * @param object : The origin object. + * @param position : The target position. + * @return True, when object can see position. + */ + public boolean canSeeLocation(WorldObject object, Location position) + { + // Get object and location coordinates. + int ox = object.getX(); + int oy = object.getY(); + int oz = object.getZ(); + int tx = position.getX(); + int ty = position.getY(); + int tz = position.getZ(); + + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld())) + { + return false; + } + + // Get object's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + // Perform geodata check. + return canSee(ox, oy, oz, oheight, tx, ty, tz, 0) && canSee(tx, ty, tz, 0, ox, oy, oz, oheight); + } + + /** + * Simple check for origin to target visibility.
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param oheight : The height of origin, used as start point. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param theight : The height of target, used as end point. + * @return True, when origin can see target. + */ + public boolean canSee(int ox, int oy, int oz, double oheight, int tx, int ty, int tz, double theight) + { + // Get origin geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get target geodata coordinates. + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check being on same cell and layer (index). + // Note: Get index must use origin height increased by cell height, the method returns index to height exclusive self. + int index = block.getIndexBelow(gox, goy, oz + GeoStructure.CELL_HEIGHT); + if (index < 0) + { + return false; + } + + if ((gox == gtx) && (goy == gty)) + { + return index == block.getIndexBelow(gtx, gty, tz + GeoStructure.CELL_HEIGHT); + } + + // Get ground and nswe flag. + int groundZ = block.getHeight(index); + int nswe = block.getNswe(index); + + // Get delta coordinates, slope of line (XY, XZ) and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double dz = (tz + theight) - (oz + oheight); + final double m = (double) dy / dx; + final double mz = dz / Math.sqrt((dx * dx) + (dy * dy)); + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); + + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) + { + // Set next cell in X direction. + gridX += mdt.getStepX(); + gox += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); - if (!canSeeThrough) - { - return false; - } + // Set next cell in Y direction. + gridY += mdt.getStepY(); + goy += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevGeoZ = curGeoZ; - ++ptIndex; + // Get block of the next cell. + block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get line of sight height (including Z slope). + double losz = oz + oheight + Config.MAX_OBSTACLE_HEIGHT; + losz += mz * Math.sqrt(((checkX - ox) * (checkX - ox)) + ((checkY - oy) * (checkY - oy))); + + // Check line of sight going though wall (vertical check). + + // Get index of particular layer, based on last iterated cell conditions. + boolean canMove = (nswe & dir) != 0; + if (canMove) + { + // No wall present, get next cell below current cell. + index = block.getIndexBelow(gox, goy, groundZ + GeoStructure.CELL_IGNORE_HEIGHT); + } + else + { + // Wall present, get next cell above current cell. + index = block.getIndexAbove(gox, goy, groundZ - (2 * GeoStructure.CELL_HEIGHT)); + } + + // Next cell's does not exist (no geodata with valid condition), return fail. + if (index < 0) + { + return false; + } + + // Get next cell's layer height. + int z = block.getHeight(index); + + // Perform sine of sight check (next cell is above line of sight line), return fail. + if (z > losz) + { + return false; + } + + // Next cell is accessible, update z and NSWE. + groundZ = z; + nswe = block.getNswe(index); } + // Iteration is completed, no obstacle is found. return true; } /** - * Move check. - * @param x the x coordinate - * @param y the y coordinate - * @param zValue the z coordinate - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tzValue the target's z coordinate - * @param instance the instance - * @return the last Location (x,y,z) where player can walk - just before wall + * Check movement from coordinates to coordinates.
+ * Note: The Z coordinates are supposed to be already validated geodata coordinates. + * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return True, when target coordinates are reachable from origin coordinates. */ - public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(x); - final int geoY = getGeoY(y); - final int z = getNearestZ(geoX, geoY, zValue); - final int tGeoX = getGeoX(tx); - final int tGeoY = getGeoY(ty); - final int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return new Location(x, y, getHeight(x, y, z)); - } - if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) - { - return new Location(x, y, getHeight(x, y, z)); + return false; } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = z; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return false; + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + int goz = getHeightNearest(gox, goy, oz); + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check movement within same cell. + if ((gox == gtx) && (goy == gty)) + { + return goz == getHeight(tx, ty, tz); + } + + // Get nswe flag. + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - // Can't move, return previous location. - return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check point heading into obstacle, if so return current point. + if ((nswe & dir) == 0) + { + return false; + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return false; + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != tz)) - { - // Different floors, return start location. - return new Location(x, y, z); - } - - return new Location(tx, ty, tz); + // When origin Z is target Z, the move is successful. + return goz == getHeight(tx, ty, tz); } /** - * Checks if its possible to move from one location to another. - * @param fromX the X coordinate to start checking from - * @param fromY the Y coordinate to start checking from - * @param fromZvalue the Z coordinate to start checking from - * @param toX the X coordinate to end checking at - * @param toY the Y coordinate to end checking at - * @param toZvalue the Z coordinate to end checking at - * @param instance the instance - * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + * Check movement from origin to target coordinates. Returns last available point in the checked path.
+ * Target X and Y reachable and Z is on same floor: + *
    + *
  • Location of the target with corrected Z value from geodata.
  • + *
+ * Target X and Y reachable but Z is on another floor: + *
    + *
  • Location of the origin with corrected Z value from geodata.
  • + *
+ * Target X and Y not reachable: + *
    + *
  • Last accessible location in destination to target.
  • + *
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return The {@link Location} representing last point of movement (e.g. just before wall). */ - public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + public Location getValidLocation(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(fromX); - final int geoY = getGeoY(fromY); - final int fromZ = getNearestZ(geoX, geoY, fromZvalue); - final int tGeoX = getGeoX(toX); - final int tGeoY = getGeoY(toY); - final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); - - if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) - { - return false; + return new Location(ox, oy, oz); } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = fromZ; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return new Location(ox, oy, oz); + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + final int gtz = getHeightNearest(gtx, gty, tz); + int goz = getHeightNearest(gox, goy, oz); + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - return false; - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check target cell is out of geodata grid (world coordinates). + if ((nx < 0) || (nx >= GeoStructure.GEO_CELLS_X) || (ny < 0) || (ny >= GeoStructure.GEO_CELLS_Y)) + { + return new Location(checkX, checkY, goz); + } + + // Check point heading into obstacle, if so return current (border) point. + if ((nswe & dir) == 0) + { + return new Location(checkX, checkY, goz); + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current (border) point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return new Location(checkX, checkY, goz); + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) - { - // Different floors. - return false; - } - - return true; + // Compare Z coordinates: + // If same, path is okay, return target point and fix its Z geodata coordinate. + // If not same, path is does not exist, return origin point. + return goz == gtz ? new Location(tx, ty, gtz) : new Location(ox, oy, oz); } + /** + * Returns the list of location objects as a result of complete path calculation. + * @param ox : origin x + * @param oy : origin y + * @param oz : origin z + * @param tx : target x + * @param ty : target y + * @param tz : target z + * @param instance + * @return {@code List} : complete path from nodes + */ + public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + { + // Get origin and check existing geo coords. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + if (!hasGeoPos(gox, goy)) + { + return Collections.emptyList(); + } + + int goz = getHeightNearest(gox, goy, oz); + + // Get target and check existing geo coords. + int gtx = getGeoX(tx); + int gty = getGeoY(ty); + if (!hasGeoPos(gtx, gty)) + { + return Collections.emptyList(); + } + + int gtz = getHeightNearest(gtx, gty, tz); + + // Prepare buffer for pathfinding calculations. + NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + if (buffer == null) + { + return Collections.emptyList(); + } + + // Find path. + List path = null; + try + { + path = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + if (path.isEmpty()) + { + return Collections.emptyList(); + } + } + catch (Exception e) + { + return Collections.emptyList(); + } + finally + { + buffer.free(); + } + + // Check path. + if (path.size() < 3) + { + return path; + } + + // Get path list iterator. + ListIterator point = path.listIterator(); + + // Get node A (origin). + int nodeAx = ox; + int nodeAy = oy; + int nodeAz = goz; + + // Get node B. + Location nodeB = point.next(); + + // Iterate thought the path to optimize it. + while (point.hasNext()) + { + // Get node C. + Location nodeC = path.get(point.nextIndex()); + + // Check movement from node A to node C. + if (canMoveToTarget(nodeAx, nodeAy, nodeAz, nodeC.getX(), nodeC.getY(), nodeC.getZ(), instance)) + { + // Can move from node A to node C. + // Remove node B. + point.remove(); + } + else + { + // Can not move from node A to node C. + // Set node A (node B is part of path, update A coordinates). + nodeAx = nodeB.getX(); + nodeAy = nodeB.getY(); + nodeAz = nodeB.getZ(); + } + + // Set node B. + nodeB = point.next(); + } + + return path; + } + + /** + * NodeBuffer container with specified size and count of separate buffers. + */ + private static class BufferHolder + { + final int _size; + final List _buffer; + + public BufferHolder(int size, int count) + { + _size = size; + _buffer = new ArrayList<>(count); + + for (int i = 0; i < count; i++) + { + _buffer.add(new NodeBuffer(size)); + } + } + } + + /** + * Returns the instance of the {@link GeoEngine}. + * @return {@link GeoEngine} : The instance. + */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -737,4 +1054,4 @@ public class GeoEngine { protected static final GeoEngine INSTANCE = new GeoEngine(); } -} +} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java deleted file mode 100644 index cc76b7523a..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ /dev/null @@ -1,301 +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 . - */ -package org.l2jmobius.gameserver.geoengine; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; -import org.l2jmobius.gameserver.model.World; -import org.l2jmobius.gameserver.model.instancezone.Instance; - -/** - * @author -Nemesiss- - */ -public class GeoEnginePathfinding -{ - private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); - - private BufferInfo[] _buffers; - - protected GeoEnginePathfinding() - { - try - { - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - - _buffers = 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); - } - - _buffers[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"); - } - } - - public boolean pathNodesExist(short regionoffset) - { - return false; - } - - public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) - { - 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)))); - if (buffer == null) - { - return null; - } - - List path = null; - try - { - final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); - - if (result == null) - { - return null; - } - - path = constructPath(result); - } - catch (Exception e) - { - LOGGER.warning(e.getMessage()); - return null; - } - finally - { - buffer.free(); - } - - // check path - if (path.size() < 3) - { - return path; - } - - int currentX, currentY, currentZ; - ListIterator middlePoint; - - middlePoint = path.listIterator(); - currentX = x; - currentY = y; - currentZ = z; - - while (middlePoint.hasNext()) - { - final AbstractNodeLoc locMiddle = middlePoint.next(); - if (!middlePoint.hasNext()) - { - break; - } - - final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); - if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) - { - middlePoint.remove(); - } - else - { - currentX = locMiddle.getX(); - currentY = locMiddle.getY(); - currentZ = locMiddle.getZ(); - } - } - - return path; - } - - /** - * Convert geodata position to pathnode position - * @param geo_pos - * @return pathnode position - */ - public short getNodePos(int geo_pos) - { - return (short) (geo_pos >> 3); // OK? - } - - /** - * Convert node position to pathnode block position - * @param node_pos - * @return pathnode block position (0...255) - */ - public short getNodeBlock(int node_pos) - { - return (short) (node_pos % 256); - } - - public byte getRegionX(int node_pos) - { - return (byte) ((node_pos >> 8) + World.TILE_X_MIN); - } - - public byte getRegionY(int node_pos) - { - return (byte) ((node_pos >> 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 node_x rx - * @return - */ - public int calculateWorldX(short node_x) - { - return World.MAP_MIN_X + (node_x * 128) + 48; - } - - /** - * Convert pathnode y to World y position - * @param node_y - * @return - */ - public int calculateWorldY(short node_y) - { - return World.MAP_MIN_Y + (node_y * 128) + 48; - } - - private List constructPath(AbstractNode nodeValue) - { - final LinkedList path = new LinkedList<>(); - int previousDirectionX = Integer.MIN_VALUE; - int previousDirectionY = Integer.MIN_VALUE; - int directionX, directionY; - - AbstractNode node = nodeValue; - while (node.getParent() != null) - { - directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); - directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); - - // only add a new route point if moving direction changes - if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) - { - previousDirectionX = directionX; - previousDirectionY = directionY; - - path.addFirst(node.getLoc()); - node.setLoc(null); - } - - node = node.getParent(); - } - - return path; - } - - private CellNodeBuffer alloc(int size) - { - CellNodeBuffer current = null; - for (BufferInfo i : _buffers) - { - if (i.mapSize >= size) - { - for (CellNodeBuffer buf : i.buffer) - { - if (buf.lock()) - { - current = buf; - break; - } - } - if (current != null) - { - break; - } - - // not found, allocate temporary buffer - current = new CellNodeBuffer(i.mapSize); - current.lock(); - if (i.buffer.size() < i.count) - { - i.buffer.add(current); - break; - } - } - } - - return current; - } - - private static final class BufferInfo - { - final int mapSize; - final int count; - ArrayList buffer; - - public BufferInfo(int size, int cnt) - { - mapSize = size; - count = cnt; - buffer = new ArrayList<>(count); - } - } - - public static GeoEnginePathfinding getInstance() - { - return SingletonHolder.INSTANCE; - } - - private static class SingletonHolder - { - protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); - } -} diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java new file mode 100644 index 0000000000..49ec35aa1c --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java @@ -0,0 +1,85 @@ +/* + * 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 . + */ +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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @param index : Index of the cell. + * @return short : Cell geodata Z coordinate, below given coordinates. + */ + public abstract byte getNswe(int index); +} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java new file mode 100644 index 0000000000..f5997bf287 --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java @@ -0,0 +1,130 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java new file mode 100644 index 0000000000..9af9d2a28a --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java @@ -0,0 +1,95 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java new file mode 100644 index 0000000000..31fbf5d477 --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java new file mode 100644 index 0000000000..f77d21d84b --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java @@ -0,0 +1,68 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +public class BlockNull extends ABlock +{ + @Override + public boolean hasGeoPos() + { + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java deleted file mode 100644 index 61ea4fcf82..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java +++ /dev/null @@ -1,48 +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 . - */ -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() - { - } -} diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java deleted file mode 100644 index 3c8de1a7f7..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java +++ /dev/null @@ -1,77 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java deleted file mode 100644 index 1ccdefe3da..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java +++ /dev/null @@ -1,56 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java new file mode 100644 index 0000000000..691b164e0c --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ +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_ALL = 0x0F; + + // 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; +} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java deleted file mode 100644 index 25eafc5a04..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java +++ /dev/null @@ -1,42 +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 . - */ -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); -} diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java deleted file mode 100644 index 0d2c765002..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java +++ /dev/null @@ -1,47 +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 . - */ -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(); -} diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java deleted file mode 100644 index 52df457360..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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); - - // 1 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; - } -} diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java deleted file mode 100644 index 72a5a635c7..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java +++ /dev/null @@ -1,55 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author HorridoJoho - */ -public final class NullRegion implements IRegion -{ - public static final NullRegion INSTANCE = new NullRegion(); - - @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 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; - } -} diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java deleted file mode 100644 index fc924cb875..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java +++ /dev/null @@ -1,92 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java deleted file mode 100644 index 5087513ed9..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java +++ /dev/null @@ -1,87 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public abstract class AbstractNode -{ - private T _loc; - private AbstractNode _parent; - - public AbstractNode(T loc) - { - _loc = loc; - } - - public void setParent(AbstractNode p) - { - _parent = p; - } - - public AbstractNode getParent() - { - return _parent; - } - - public T getLoc() - { - return _loc; - } - - public void setLoc(T l) - { - _loc = l; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); - return result; - } - - @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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java deleted file mode 100644 index 7223b5b2be..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java +++ /dev/null @@ -1,33 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -/** - * @author -Nemesiss- - */ -public abstract class AbstractNodeLoc -{ - public abstract int getX(); - - public abstract int getY(); - - public abstract int getZ(); - - public abstract int getNodeX(); - - public abstract int getNodeY(); -} diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java deleted file mode 100644 index 3425234e0e..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public class CellNode extends AbstractNode -{ - 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java deleted file mode 100644 index 522610bb7c..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java +++ /dev/null @@ -1,348 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -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 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) - { - _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(); - } - - public final List debugPath() - { - final List 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 void getNeighbors() - { - if (!_current.getLoc().canGoAll()) - { - 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); - } - - // SouthEast - if ((nodeE != null) && (nodeS != null)) - { - if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) - { - addNode(x + 1, y + 1, z, true); - } - } - - // SouthWest - if ((nodeS != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) - { - addNode(x - 1, y + 1, z, true); - } - } - - // NorthEast - if ((nodeN != null) && (nodeE != null)) - { - if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) - { - addNode(x + 1, y - 1, z, true); - } - } - - // NorthWest - if ((nodeN != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) - { - addNode(x - 1, y - 1, z, true); - } - } - } - - private 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(); - // reinit node if needed - if (result.getLoc() != null) - { - result.getLoc().set(x, y, z); - } - else - { - result.setLoc(new NodeLoc(x, y, z)); - } - } - - return result; - } - - private 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 boolean isHighWeight(int x, int y, int z) - { - final CellNode result = getNode(x, y, z); - if (result == null) - { - return true; - } - - if (!result.getLoc().canGoAll()) - { - return true; - } - if (Math.abs(result.getLoc().getZ() - z) > 16) - { - return true; - } - - return false; - } - - private 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java new file mode 100644 index 0000000000..fa5ca43c2f --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -0,0 +1,111 @@ +/* + * 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 . + */ +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 geodata values. + private int _geoX; + private int _geoY; + private byte _nswe; + + // 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; + + _costG = 0; + _costH = 0; + _costF = 0; + + _parent = null; + } + + public void setGeo(int gx, int gy, int gz, byte nswe) + { + super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); + + _geoX = gx; + _geoY = gy; + _nswe = nswe; + } + + 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 int getCostF() + { + return _costF; + } + + public Node getParent() + { + return _parent; + } + + @Override + public int compareTo(Node o) + { + return _costF - o._costF; + } +} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java new file mode 100644 index 0000000000..b464212c96 --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -0,0 +1,368 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.awt.Color; +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; +import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; + +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 _opened; + // Container holding Nodes already explored. + private final List _closed; + + // Target coordinates. + private int _gtx; + private int _gty; + private int _gtz; + + // Pathfinding statistics. + private long _timeStamp; + private long _lastElapsedTime; + + 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 findPath(int gox, int goy, int goz, int gtx, int gty, int gtz) + { + // Set start timestamp. + _timeStamp = System.currentTimeMillis(); + + // 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)); + _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 constructPath() + { + // Create result. + final LinkedList 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; + } + + /** + * Creates list of Nodes to show debug path. + * @param debug : The debug packet to add debug informations in. + */ + public void debugPath(ExServerPrimitive debug) + { + // Add all opened node as yellow points. + for (Node n : _opened) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); + } + + // Add all opened node as blue points. + for (Node n : _closed) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); + } + } + + 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; + + _lastElapsedTime = System.currentTimeMillis() - _timeStamp; + _lock.unlock(); + } + + public long getElapsedTime() + { + return _lastElapsedTime; + } + + /** + * Expand the current {@link Node} by exploring its neighbors (axially and diagonally). + */ + private void expand() + { + // Movement is blocked, skip. + final byte nswe = _current.getNSWE(); + if (nswe == 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; + + // Can move north, expand. + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + } + + // Can move south, expand. + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + } + + // Can move west, expand. + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move east, expand. + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move north-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move north-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + } + + /** + * 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 weight : The weight of movement to the new node. + * @return The nswe of the added node. Blank, if not added. + */ + private byte addNode(int gx, int gy, int gzValue, int weight) + { + // 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]; + + // Set node geodata coordinates. + node.setGeo(gx, gy, gz, nswe); + + // 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, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + _opened.add(node); + _bufferIndex++; + return nswe; + } + + /** + * Calculate cost H value, calculated using diagonal distance method.
+ * 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. + final int dx = Math.abs(gx - _gtx); + final int dy = Math.abs(gy - _gty); + final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + + // Get diagonal and axial differences to the target. + final int dd = Math.min(dx, dy); + final int da = Math.max(dx, dy) - dd; + + // Calculate the diagonal distance of the node to the target. + return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + } +} \ No newline at end of file diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java deleted file mode 100644 index e3bad04b25..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; - -/** - * @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 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 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; - // return super.hashCode(); - } - - @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; - if (_x != other._x) - { - return false; - } - if (_y != other._y) - { - return false; - } - if (_goNorth != other._goNorth) - { - return false; - } - if (_goEast != other._goEast) - { - return false; - } - if (_goSouth != other._goSouth) - { - return false; - } - if (_goWest != other._goWest) - { - return false; - } - if (_geoHeight != other._geoHeight) - { - return false; - } - return true; - } -} diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java index 1faf1ae423..ed261765e1 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java @@ -93,15 +93,15 @@ public class ZoneManager implements IXmlReader private static final Map SETTINGS = new HashMap<>(); private static final int SHIFT_BY = 15; - private static final int OFFSET_X = Math.abs(World.MAP_MIN_X >> SHIFT_BY); - private static final int OFFSET_Y = Math.abs(World.MAP_MIN_Y >> SHIFT_BY); + private static final int OFFSET_X = Math.abs(World.WORLD_X_MIN >> SHIFT_BY); + private static final int OFFSET_Y = Math.abs(World.WORLD_Y_MIN >> SHIFT_BY); private final Map, ConcurrentHashMap> _classZones = new ConcurrentHashMap<>(); private final Map _spawnTerritories = new ConcurrentHashMap<>(); private final AtomicInteger _lastDynamicId = new AtomicInteger(300000); private List _debugItems; - private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.MAP_MAX_X >> SHIFT_BY) + OFFSET_X + 1][(World.MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y + 1]; + private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.WORLD_X_MAX >> SHIFT_BY) + OFFSET_X + 1][(World.WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y + 1]; /** * Instantiates a new zone manager. diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/Location.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/Location.java index 73689ab1a6..ca3b22a3f0 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/Location.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/Location.java @@ -16,30 +16,30 @@ */ package org.l2jmobius.gameserver.model; +import java.util.Objects; + +import org.l2jmobius.commons.util.Point2D; import org.l2jmobius.gameserver.model.interfaces.ILocational; import org.l2jmobius.gameserver.model.interfaces.IPositionable; /** - * Location data transfer object.
- * Contains coordinates data, heading and instance Id. - * @author Zoey76 + * A datatype used to retain a 3D (x/y/z/heading) point. It got the capability to be set and cleaned. */ -public class Location implements IPositionable +public class Location extends Point2D implements IPositionable { - protected int _x; - protected int _y; - protected int _z; - private int _heading; + protected volatile int _z; + protected volatile int _heading; public Location(int x, int y, int z) { - this(x, y, z, 0); + super(x, y); + _z = z; + _heading = 0; } public Location(int x, int y, int z, int heading) { - _x = x; - _y = y; + super(x, y); _z = z; _heading = heading; } @@ -51,9 +51,8 @@ public class Location implements IPositionable public Location(StatSet set) { - _x = set.getInt("x"); - _y = set.getInt("y"); - _z = set.getInt("z"); + super(set.getInt("x", 0), set.getInt("y", 0)); + _z = set.getInt("z", 0); _heading = set.getInt("heading", 0); } @@ -146,6 +145,25 @@ public class Location implements IPositionable _heading = loc.getHeading(); } + @Override + public void clean() + { + super.clean(); + _z = 0; + } + + @Override + public Location clone() + { + return new Location(_x, _y, _z); + } + + @Override + public int hashCode() + { + return (31 * super.hashCode()) + Objects.hash(_z); + } + @Override public boolean equals(Object obj) { diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/World.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/World.java index c1510e8c0e..0d796dd834 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/World.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/World.java @@ -66,19 +66,19 @@ public class World public static final int TILE_Y_MAX = 26; public static final int TILE_ZERO_COORD_X = 20; public static final int TILE_ZERO_COORD_Y = 18; - public static final int MAP_MIN_X = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; - public static final int MAP_MIN_Y = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; + public static final int WORLD_X_MIN = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; + public static final int WORLD_Y_MIN = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; - public static final int MAP_MAX_X = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; - public static final int MAP_MAX_Y = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; + public static final int WORLD_X_MAX = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; + public static final int WORLD_Y_MAX = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; /** Calculated offset used so top left region is 0,0 */ - public static final int OFFSET_X = Math.abs(MAP_MIN_X >> SHIFT_BY); - public static final int OFFSET_Y = Math.abs(MAP_MIN_Y >> SHIFT_BY); + public static final int OFFSET_X = Math.abs(WORLD_X_MIN >> SHIFT_BY); + public static final int OFFSET_Y = Math.abs(WORLD_Y_MIN >> SHIFT_BY); /** Number of regions. */ - private static final int REGIONS_X = (MAP_MAX_X >> SHIFT_BY) + OFFSET_X; - private static final int REGIONS_Y = (MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y; + private static final int REGIONS_X = (WORLD_X_MAX >> SHIFT_BY) + OFFSET_X; + private static final int REGIONS_Y = (WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y; /** Map containing all the players in game. */ private static final Map _allPlayers = new ConcurrentHashMap<>(); @@ -817,9 +817,6 @@ public class World return _memberInPartyNumber.get(); } - /** - * @return the current instance of World - */ public static World getInstance() { return SingletonHolder.INSTANCE; diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/WorldObject.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/WorldObject.java index 0e5536bbc0..62313ffea1 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/WorldObject.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/WorldObject.java @@ -188,23 +188,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif synchronized (this) { int spawnX = x; - if (spawnX > World.MAP_MAX_X) + if (spawnX > World.WORLD_X_MAX) { - spawnX = World.MAP_MAX_X - 5000; + spawnX = World.WORLD_X_MAX - 5000; } - if (spawnX < World.MAP_MIN_X) + if (spawnX < World.WORLD_X_MIN) { - spawnX = World.MAP_MIN_X + 5000; + spawnX = World.WORLD_X_MIN + 5000; } int spawnY = y; - if (spawnY > World.MAP_MAX_Y) + if (spawnY > World.WORLD_Y_MAX) { - spawnY = World.MAP_MAX_Y - 5000; + spawnY = World.WORLD_Y_MAX - 5000; } - if (spawnY < World.MAP_MIN_Y) + if (spawnY < World.WORLD_Y_MIN) { - spawnY = World.MAP_MIN_Y + 5000; + spawnY = World.WORLD_Y_MIN + 5000; } // Set the x,y,z position of the WorldObject. If flagged with _isSpawned, setXYZ will automatically update world region, so avoid that. @@ -518,23 +518,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif public void setXYZInvisible(int x, int y, int z) { int correctX = x; - if (correctX > World.MAP_MAX_X) + if (correctX > World.WORLD_X_MAX) { - correctX = World.MAP_MAX_X - 5000; + correctX = World.WORLD_X_MAX - 5000; } - if (correctX < World.MAP_MIN_X) + if (correctX < World.WORLD_X_MIN) { - correctX = World.MAP_MIN_X + 5000; + correctX = World.WORLD_X_MIN + 5000; } int correctY = y; - if (correctY > World.MAP_MAX_Y) + if (correctY > World.WORLD_Y_MAX) { - correctY = World.MAP_MAX_Y - 5000; + correctY = World.WORLD_Y_MAX - 5000; } - if (correctY < World.MAP_MIN_Y) + if (correctY < World.WORLD_Y_MIN) { - correctY = World.MAP_MIN_Y + 5000; + correctY = World.WORLD_Y_MIN + 5000; } setXYZ(correctX, correctY, z); diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/Creature.java index 47987924ee..127e8c7644 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -65,8 +65,6 @@ 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.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -2585,7 +2583,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3406,8 +3404,8 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe final int originalX = x; final int originalY = y; final int originalZ = z; - final int gtx = (originalX - World.MAP_MIN_X) >> 4; - final int gty = (originalY - World.MAP_MIN_Y) >> 4; + final int gtx = (originalX - World.WORLD_X_MIN) >> 4; + final int gty = (originalY - World.WORLD_Y_MIN) >> 4; if (isOnGeodataPath()) { try @@ -3430,7 +3428,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe && !(((curZ - z) > 300) && (distance < 300))) // Prohibit correcting destination if character wants to fall. { // location different if destination wasn't reached (or just z coord is different) - final Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(curX, curY, curZ, x, y, z, getInstanceWorld()); + final Location destiny = GeoEngine.getInstance().getValidLocation(curX, curY, curZ, x, y, z, getInstanceWorld()); x = destiny.getX(); y = destiny.getY(); z = destiny.getZ(); @@ -3444,7 +3442,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { if (isPlayer() && !_isFlying && !isInWater) diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/Summon.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/Summon.java index 6065b24f61..919cda941e 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/Summon.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/actor/Summon.java @@ -106,7 +106,7 @@ public abstract class Summon extends Playable final int x = owner.getX(); final int y = owner.getY(); final int z = owner.getZ(); - final Location location = GeoEngine.getInstance().canMoveToTargetLoc(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, owner.getInstanceWorld()); + final Location location = GeoEngine.getInstance().getValidLocation(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, getInstanceWorld()); setXYZInvisible(location.getX(), location.getY(), location.getZ()); } diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java index abbe632775..8943842d96 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java @@ -1569,7 +1569,7 @@ public class ItemInstance extends WorldObject if (dropper != null) { final Instance instance = dropper.getInstanceWorld(); - final Location dropDest = GeoEngine.getInstance().canMoveToTargetLoc(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); + final Location dropDest = GeoEngine.getInstance().getValidLocation(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); x = dropDest.getX(); y = dropDest.getY(); z = dropDest.getZ(); diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java index 4659b0a0cd..a7066c227f 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java @@ -1179,7 +1179,7 @@ public class SkillCaster implements Runnable } } - final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().canMoveToTargetLoc(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); + final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().getValidLocation(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); creature.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); creature.broadcastPacket(new FlyToLocation(creature, destination, flyType, 0, 0, 333)); diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java index c62c821f44..30068134e3 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java @@ -114,7 +114,7 @@ public class ValidatePosition implements IClientIncomingPacket { if (player.isFalling(_z)) { - final int nearestZ = GeoEngine.getInstance().getHigherHeight(_x, _y, _z); + final int nearestZ = GeoEngine.getInstance().getHeight(_x, _y, _z); if (player.getZ() < nearestZ) { player.setXYZ(_x, _y, nearestZ); diff --git a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/util/GeoUtils.java index 4600a0fc53..19a979819e 100644 --- a/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_6.0_Fafurion/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -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.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; @@ -30,21 +30,21 @@ public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getInstance().getWorldX(iter.x()); - final int wy = GeoEngine.getInstance().getWorldY(iter.y()); + final int wx = GeoEngine.getWorldX(iter.x()); + final int wy = GeoEngine.getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public final class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); - int wy = GeoEngine.getInstance().getWorldY(prevY); + int wx = GeoEngine.getWorldX(prevX); + int wy = GeoEngine.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.getInstance().getWorldX(curX); - wy = GeoEngine.getInstance().getWorldY(curY); + wx = GeoEngine.getWorldX(curX); + wy = GeoEngine.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().checkNearestNswe(x, y, z, nswe)) + if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0) { return Color.GREEN; } @@ -109,9 +109,8 @@ public final class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final GeoEngine ge = GeoEngine.getInstance(); - final int playerGx = ge.getGeoX(player.getX()); - final int playerGy = ge.getGeoY(player.getY()); + final int playerGx = GeoEngine.getGeoX(player.getX()); + final int playerGy = GeoEngine.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -135,32 +134,32 @@ public final class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = ge.getWorldX(gx); - final int y = ge.getWorldY(gy); - final int z = ge.getNearestZ(gx, gy, player.getZ()); + final int x = GeoEngine.getWorldX(gx); + final int y = GeoEngine.getWorldY(gy); + final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); + Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); 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, Cell.NSWE_EAST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); 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, Cell.NSWE_SOUTH); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); 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, Cell.NSWE_WEST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); 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); @@ -188,41 +187,41 @@ public final class GeoUtils { if (y > lastY) { - return Cell.NSWE_SOUTH_EAST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E; } else if (y < lastY) { - return Cell.NSWE_NORTH_EAST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E; } else { - return Cell.NSWE_EAST; + return GeoStructure.CELL_FLAG_E; } } else if (x < lastX) // west { if (y > lastY) { - return Cell.NSWE_SOUTH_WEST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W; } else if (y < lastY) { - return Cell.NSWE_NORTH_WEST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W; } else { - return Cell.NSWE_WEST; + return GeoStructure.CELL_FLAG_W; } } else // unchanged x { if (y > lastY) { - return Cell.NSWE_SOUTH; + return GeoStructure.CELL_FLAG_S; } else if (y < lastY) { - return Cell.NSWE_NORTH; + return GeoStructure.CELL_FLAG_N; } else { diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/config/GeoEngine.ini b/L2J_Mobius_7.0_PreludeOfWar/dist/game/config/GeoEngine.ini index 609e8a89a7..e740c678dd 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/config/GeoEngine.ini @@ -6,33 +6,44 @@ # at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ 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 + # ================================================================= -# Path finding +# Pathfinding # ================================================================= # 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 +# an alternative path (e.g. walk around obstacle), default: True +PathFinding = True -# Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 -PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 +# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 -# Weight for nodes without obstacles far from walls -LowWeight = 0.5 +# Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 +MoveWeight = 10 +MoveWeightDiag = 14 -# Weight for nodes near walls -MediumWeight = 2 +# 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 -# Weight for nodes with obstacles -HighWeight = 3 +# 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. -# Default: LowWeight * sqrt(2) -DiagonalWeight = 0.707 +# Maximum number of generated nodes per one path-finding process, default 3500 +MaxIterations = 3500 # ================================================================= -# Other +# Line of Sight # ================================================================= -# Correct player Z after falling through the ground. -CorrectPlayerZ = False +# 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 diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/geodata/L2D_GeoEngine.patch b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/geodata/L2D_GeoEngine.patch deleted file mode 100644 index f8577e3a0e..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/geodata/L2D_GeoEngine.patch +++ /dev/null @@ -1,6035 +0,0 @@ -Index: dist/game/GeoDataConverter.bat -=================================================================== ---- dist/game/GeoDataConverter.bat (nonexistent) -+++ dist/game/GeoDataConverter.bat (working copy) -@@ -0,0 +1,6 @@ -+@echo off -+title L2D geodata converter -+ -+java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter -+ -+pause -Index: dist/game/GeoDataConverter.sh -=================================================================== ---- dist/game/GeoDataConverter.sh (nonexistent) -+++ dist/game/GeoDataConverter.sh (working copy) -@@ -0,0 +1,4 @@ -+#! /bin/sh -+ -+java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 -+ -Index: dist/game/config/GeoEngine.ini -=================================================================== ---- dist/game/config/GeoEngine.ini (revision 8409) -+++ dist/game/config/GeoEngine.ini (working copy) -@@ -1,6 +1,10 @@ - # ================================================================= - # Geodata - # ================================================================= -+# Because of real-time performance we are using geodata files only in -+# diagonal L2D format now (using filename e.g. 22_16.l2d). -+# L2D geodata can be obtained by conversion of existing L2J or L2OFF geodata. -+# Launch "GeoDataConverter.bat/sh" and follow instructions to start the conversion. - - # 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/ -@@ -13,6 +17,16 @@ - CoordSynchronize = 2 - - # ================================================================= -+# Path checking -+# ================================================================= -+ -+# 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 finding - # ================================================================= - -@@ -23,19 +37,23 @@ - # Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - --# Weight for nodes without obstacles far from walls --LowWeight = 0.5 -+# Base path weight, when moving from one node to another on axis direction, default: 10 -+BaseWeight = 10 - --# Weight for nodes near walls --MediumWeight = 2 -+# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -+DiagonalWeight = 14 - --# Weight for nodes with obstacles --HighWeight = 3 -+# When movement flags of target node is blocked to any direction, multiply movement weight by this multiplier. -+# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 10 -+ObstacleMultiplier = 10 - --# Weight for diagonal movement. --# Default: LowWeight * sqrt(2) --DiagonalWeight = 0.707 -+# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 20 -+# For proper function must be higher than BaseWeight and/or DiagonalWeight. -+HeuristicWeight = 20 - -+# Maximum number of generated nodes per one path-finding process, default 3500 -+MaxIterations = 3500 -+ - # ================================================================= - # Other - # ================================================================= -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (working copy) -@@ -55,9 +55,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -@@ -73,9 +72,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (working copy) -@@ -18,11 +18,11 @@ - - import java.util.List; - --import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -+import org.l2jmobius.gameserver.geoengine.GeoEngine; - import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -+import org.l2jmobius.gameserver.network.SystemMessageId; - import org.l2jmobius.gameserver.util.BuilderUtil; - - public class AdminPathNode implements IAdminCommandHandler -@@ -37,29 +37,30 @@ - { - if (command.equals("admin_path_find")) - { -- if (!Config.PATHFINDING) -- { -- BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); -- return true; -- } - if (activeChar.getTarget() != null) - { -- final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); -+ final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); - if (path == null) - { -- BuilderUtil.sendSysMessage(activeChar, "No Route!"); -- return true; -+ BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); - } -- for (AbstractNodeLoc a : path) -+ else - { -- BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); -+ for (Location point : path) -+ { -+ BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); -+ } - } - } - else - { -- BuilderUtil.sendSysMessage(activeChar, "No Target!"); -+ activeChar.sendPacket(SystemMessageId.INVALID_TARGET); - } - } -+ else -+ { -+ return false; -+ } - return true; - } - -Index: java/org/l2jmobius/Config.java -=================================================================== ---- java/org/l2jmobius/Config.java (revision 8409) -+++ java/org/l2jmobius/Config.java (working copy) -@@ -32,7 +32,6 @@ - import java.net.UnknownHostException; - import java.nio.charset.StandardCharsets; - import java.nio.file.Files; --import java.nio.file.Path; - import java.nio.file.Paths; - import java.time.Duration; - import java.util.ArrayList; -@@ -966,14 +965,17 @@ - // -------------------------------------------------- - // GeoEngine - // -------------------------------------------------- -- public static Path GEODATA_PATH; -+ public static String GEODATA_PATH; -+ public static int COORD_SYNCHRONIZE; -+ public static int PART_OF_CHARACTER_HEIGHT; -+ public static int MAX_OBSTACLE_HEIGHT; - public static boolean PATHFINDING; - public static String PATHFIND_BUFFERS; -- public static float LOW_WEIGHT; -- public static float MEDIUM_WEIGHT; -- public static float HIGH_WEIGHT; -- public static float DIAGONAL_WEIGHT; -- public static int COORD_SYNCHRONIZE; -+ public static int BASE_WEIGHT; -+ public static int DIAGONAL_WEIGHT; -+ public static int HEURISTIC_WEIGHT; -+ public static int OBSTACLE_MULTIPLIER; -+ public static int MAX_ITERATIONS; - public static boolean CORRECT_PLAYER_Z; - - /** Attribute System */ -@@ -2568,14 +2570,17 @@ - - // Load GeoEngine config file (if exists) - final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); -- GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); -+ GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); -+ COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); -+ MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); - PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -- LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); -- MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); -- HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); -- DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); -- COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); -+ DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); -+ OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); -+ HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); -+ MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); - CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); - - // Load AllowedPlayerRaces config file (if exists) -Index: java/org/l2jmobius/gameserver/geoengine/GeoEngine.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (revision 8435) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (working copy) -@@ -16,100 +16,90 @@ - */ - package org.l2jmobius.gameserver.geoengine; - --import java.io.IOException; -+import java.io.File; - import java.io.RandomAccessFile; - import java.nio.ByteOrder; --import java.nio.channels.FileChannel.MapMode; --import java.nio.file.Files; --import java.nio.file.Path; --import java.util.concurrent.atomic.AtomicReferenceArray; --import java.util.logging.Level; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.List; - import java.util.logging.Logger; - - import org.l2jmobius.Config; - import org.l2jmobius.gameserver.data.xml.DoorData; - import org.l2jmobius.gameserver.data.xml.FenceData; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; --import org.l2jmobius.gameserver.geoengine.geodata.IRegion; --import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; --import org.l2jmobius.gameserver.geoengine.geodata.Region; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.World; - import org.l2jmobius.gameserver.model.WorldObject; -+import org.l2jmobius.gameserver.model.actor.Creature; - import org.l2jmobius.gameserver.model.instancezone.Instance; --import org.l2jmobius.gameserver.model.interfaces.ILocational; --import org.l2jmobius.gameserver.util.GeoUtils; --import org.l2jmobius.gameserver.util.LinePointIterator; --import org.l2jmobius.gameserver.util.LinePointIterator3D; -+import org.l2jmobius.gameserver.util.MathUtil; - - /** -- * @author -Nemesiss-, HorridoJoho -+ * @author Hasha - */ - public class GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); -+ protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - -- private static final int WORLD_MIN_X = -655360; -- private static final int WORLD_MIN_Y = -589824; -- private static final int WORLD_MIN_Z = -16384; -+ private final ABlock[][] _blocks; -+ private final BlockNull _nullBlock; - -- /** 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; -- -- /** The regions array */ -- private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); -- -- private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; -- private static final int ELEVATED_SEE_OVER_DISTANCE = 2; -- private static final int MAX_SEE_OVER_HEIGHT = 48; -- -- protected GeoEngine() -+ /** -+ * GeoEngine constructor. Loads all geodata files of chosen geodata format. -+ */ -+ public GeoEngine() - { - LOGGER.info("GeoEngine: Initializing..."); -- for (int i = 0; i < _regions.length(); i++) -- { -- _regions.set(i, NullRegion.INSTANCE); -- } - -+ // initialize block container -+ _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; -+ -+ // load null block -+ _nullBlock = new BlockNull(); -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files according to geoengine config setup - int loaded = 0; -- try -+ long fileSize = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { -- for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { -- for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) -+ final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); -+ if (f.exists() && !f.isDirectory()) - { -- final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); -- if (Files.exists(geoFilePath)) -+ // region file is load-able, try to load it -+ if (loadGeoBlocks(rx, ry)) - { -- try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) -- { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -- loaded++; -- } -+ loaded++; -+ fileSize += f.length(); - } - } -+ else -+ { -+ // region file is not load-able, load null blocks -+ loadNullBlocks(rx, ry); -+ } - } - } -- catch (Exception e) -+ -+ LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -+ if (loaded > 0) - { -- LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); -- System.exit(1); -+ LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); - } - -- LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -- -- // Avoid wrong configs when no files are loaded. -+ // avoid wrong configs when no files are loaded - if (loaded == 0) - { - if (Config.PATHFINDING) -@@ -123,619 +113,820 @@ - LOGGER.info("GeoEngine: Forcing CoordSynchronize setting to -1."); - } - } -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); - } - - /** -- * @param geoX -- * @param geoY -- * @return the region -+ * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. -+ * @return boolean : True, when geodata file was loaded without problem. - */ -- private IRegion getRegion(int geoX, int geoY) -+ private final boolean loadGeoBlocks(int regionX, int regionY) - { -- final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); -- if ((region < 0) || (region >= _regions.length())) -+ final String filename = String.format(GeoFormat.L2D.getFilename(), regionX, regionY); -+ -+ // standard load -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) - { -- return null; -+ // initialize file buffer -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, GeoFormat.L2D); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ } -+ -+ // check data consistency -+ if (buffer.remaining() > 0) -+ { -+ LOGGER.warning("GeoEngine: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ } -+ -+ // loading was successful -+ return true; - } -- return _regions.get(region); -- } -- -- /** -- * @param filePath -- * @param regionX -- * @param regionY -- * @throws IOException -- */ -- 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")) -+ catch (Exception e) - { -- _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -+ // an error occured while loading, load null blocks -+ LOGGER.warning("GeoEngine: Error while loading " + filename + " region file."); -+ LOGGER.warning(e.getMessage()); -+ -+ // replace whole region file with null blocks -+ loadNullBlocks(regionX, regionY); -+ -+ // loading was not successful -+ return false; - } - } - - /** -- * @param regionX -- * @param regionY -+ * Loads null blocks. Used when no region file is detected or an error occurs during loading. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. - */ -- public void unloadRegion(int regionX, int regionY) -+ private final void loadNullBlocks(int regionX, int regionY) - { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); -- } -- -- /** -- * @param geoX -- * @param geoY -- * @return if geodata exist -- */ -- public boolean hasGeoPos(int geoX, int geoY) -- { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // load all null blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { -- return false; -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[blockX + ix][blockY + iy] = _nullBlock; -+ } - } -- return region.hasGeo(); - } - -+ // GEODATA - GENERAL -+ - /** -- * Checks the specified position for available geodata. -- * @param x the world x -- * @param y the world y -- * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise -+ * Converts world X to geodata X. -+ * @param worldX -+ * @return int : Geo X - */ -- public boolean hasGeo(int x, int y) -+ public static int getGeoX(int worldX) - { -- return hasGeoPos(getGeoX(x), getGeoY(y)); -+ return (MathUtil.limit(worldX, World.MAP_MIN_X, World.MAP_MAX_X) - World.MAP_MIN_X) >> 4; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe check -+ * Converts world Y to geodata Y. -+ * @param worldY -+ * @return int : Geo Y - */ -- public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) -+ public static int getGeoY(int worldY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return true; -- } -- return region.checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(worldY, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.MAP_MIN_Y) >> 4; - } - - /** -+ * Converts geodata X to world X. - * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe anti-corner cut -+ * @return int : World X - */ -- public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) -+ public static int getWorldX(int geoX) - { -- boolean can = true; -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- return can && checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(geoX, 0, GeoStructure.GEO_CELLS_X) << 4) + World.MAP_MIN_X + 8; - } - - /** -- * @param geoX -+ * Converts geodata Y to world Y. - * @param geoY -- * @param worldZ -- * @return the nearest Z value -+ * @return int : World Y - */ -- public int getNearestZ(int geoX, int geoY, int worldZ) -+ public static int getWorldY(int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return worldZ; -- } -- return region.getNearestZ(geoX, geoY, worldZ); -+ return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next lower Z value -+ * Returns block of geodata on given coordinates. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return {@link ABlock} : Block of geodata. - */ -- public int getNextLowerZ(int geoX, int geoY, int worldZ) -+ private final ABlock getBlock(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final int x = geoX / GeoStructure.BLOCK_CELLS_X; -+ final int y = geoY / GeoStructure.BLOCK_CELLS_Y; -+ -+ // if x or y is out of array return null -+ if ((x > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) - { -- return worldZ; -+ return _blocks[x][y]; - } -- return region.getNextLowerZ(geoX, geoY, worldZ); -+ return null; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next higher Z value -+ * Check if geo coordinates has geo. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return boolean : True, if given geo coordinates have geodata - */ -- public int getNextHigherZ(int geoX, int geoY, int worldZ) -+ public boolean hasGeoPos(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final ABlock block = getBlock(geoX, geoY); -+ if (block == null) // null block check - { -- return worldZ; -+ // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) -+ // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); -+ return false; - } -- return region.getNextHigherZ(geoX, geoY, worldZ); -+ return block.hasGeoPos(); - } - - /** -- * Gets the Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the height of cell, which is closest to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, closest to given coordinates. - */ -- public int getHeight(int x, int y, int z) -+ public short getHeightNearest(int geoX, int geoY, int worldZ) - { -- return getNearestZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getHeightNearest(geoX, geoY, worldZ) : (short) worldZ; - } - - /** -- * Gets the next lower Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the NSWE flag byte of cell, which is closes to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. - */ -- public int getLowerHeight(int x, int y, int z) -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) - { -- return getNextLowerZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getNsweNearest(geoX, geoY, worldZ) : (byte) 0xFF; - } - - /** -- * Gets the next higher Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Check if world coordinates has geo. -+ * @param worldX : World X -+ * @param worldY : World Y -+ * @return boolean : True, if given world coordinates have geodata - */ -- public int getHigherHeight(int x, int y, int z) -+ public boolean hasGeo(int worldX, int worldY) - { -- return getNextHigherZ(getGeoX(x), getGeoY(y), z); -+ return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); - } - - /** -- * @param worldX -- * @return the geo X -+ * Returns closest Z coordinate according to geodata. -+ * @param worldX : world x -+ * @param worldY : world y -+ * @param worldZ : world z -+ * @return short : nearest Z coordinates according to geodata - */ -- public int getGeoX(int worldX) -+ public short getHeight(int worldX, int worldY, int worldZ) - { -- return (worldX - WORLD_MIN_X) / 16; -+ return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - -- /** -- * @param worldY -- * @return the geo Y -- */ -- public int getGeoY(int worldY) -- { -- return (worldY - WORLD_MIN_Y) / 16; -- } -+ // PATHFINDING - - /** -- * @param worldZ -- * @return the geo Z -+ * Check line of sight from {@link WorldObject} to {@link WorldObject}. -+ * @param origin : The origin object. -+ * @param target : The target object. -+ * @return {@code boolean} : True if origin can see target - */ -- public int getGeoZ(int worldZ) -+ public boolean canSeeTarget(WorldObject origin, WorldObject target) - { -- return (worldZ - WORLD_MIN_Z) / 16; -- } -- -- /** -- * @param geoX -- * @return the world X -- */ -- public int getWorldX(int geoX) -- { -- return (geoX * 16) + WORLD_MIN_X + 8; -- } -- -- /** -- * @param geoY -- * @return the world Y -- */ -- public int getWorldY(int geoY) -- { -- return (geoY * 16) + WORLD_MIN_Y + 8; -- } -- -- /** -- * @param geoZ -- * @return the world Z -- */ -- public int getWorldZ(int geoZ) -- { -- return (geoZ * 16) + WORLD_MIN_Z + 8; -- } -- -- /** -- * Can see target. Doors as target always return true. Checks doors between. -- * @param cha the character -- * @param target the target -- * @return {@code true} if the character can see the target (LOS), {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, WorldObject target) -- { -- if (target.isDoor()) -+ if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) - { -- // Can always see doors. - return true; - } -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param cha the character -- * @param worldPosition the world position -- * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) -- { -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param world -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @param tworld the target's instanceId -- * @return -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) -- { -- return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instance -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) -- { -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) -+ -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = target.getX(); -+ final int ty = target.getY(); -+ final int tz = target.getZ(); -+ -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } -- return canSeeTarget(x, y, z, tx, ty, tz); -- } -- -- /** -- * @param prevX -- * @param prevY -- * @param prevGeoZ -- * @param curX -- * @param curY -- * @param nswe -- * @return the LOS Z value -- */ -- private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) -- { -- if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { -- throw new RuntimeException("Multiple directions!"); -+ return false; - } - -- if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- return getNearestZ(curX, curY, prevGeoZ); -+ return true; - } - -- return getNextHigherZ(curX, curY, prevGeoZ); -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) -+ { -+ return true; -+ } -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) -+ { -+ return goz == gtz; -+ } -+ -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) -+ { -+ oheight = ((Creature) origin).getCollisionHeight() * 2; -+ } -+ -+ double theight = 0; -+ if (target.isCreature()) -+ { -+ theight = ((Creature) target).getCollisionHeight() * 2; -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); - } - - /** -- * Can see target. Does not check doors between. -- * @param xValue the x coordinate -- * @param yValue the y coordinate -- * @param zValue the z coordinate -- * @param txValue the target's x coordinate -- * @param tyValue the target's y coordinate -- * @param tzValue the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -+ * Check line of sight from {@link WorldObject} to {@link Location}. -+ * @param origin : The origin object. -+ * @param position : The target position. -+ * @return {@code boolean} : True if object can see position - */ -- public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) -+ public boolean canSeeTarget(WorldObject origin, Location position) - { -- int x = xValue; -- int y = yValue; -- int tx = txValue; -- int ty = tyValue; -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = position.getX(); -+ final int ty = position.getY(); -+ final int tz = position.getZ(); - -- int geoX = getGeoX(x); -- int geoY = getGeoY(y); -- int tGeoX = getGeoX(tx); -- int tGeoY = getGeoY(ty); -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) -+ { -+ return false; -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) -+ { -+ return false; -+ } - -- int z = getNearestZ(geoX, geoY, zValue); -- int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- // Fastpath. -- if ((geoX == tGeoX) && (geoY == tGeoY)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- if (hasGeoPos(tGeoX, tGeoY)) -- { -- return z == tz; -- } - return true; - } - -- if (tz > z) -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) - { -- int tmp = tx; -- tx = x; -- x = tmp; -- -- tmp = ty; -- ty = y; -- y = tmp; -- -- tmp = tz; -- tz = z; -- z = tmp; -- -- tmp = tGeoX; -- tGeoX = geoX; -- geoX = tmp; -- -- tmp = tGeoY; -- tGeoY = geoY; -- geoY = tmp; -+ return goz == gtz; - } - -- final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); -- // First point is guaranteed to be available, skip it, we can always see our own position. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- final int prevZ = pointIter.z(); -- int prevGeoZ = prevZ; -- int ptIndex = 0; -- while (pointIter.next()) -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight(); -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); -+ } -+ -+ /** -+ * Simple check for origin to target visibility. -+ * @param goxValue : origin X geodata coordinate -+ * @param goyValue : origin Y geodata coordinate -+ * @param gozValue : origin Z geodata coordinate -+ * @param oheight : origin height (if instance of {@link Character}) -+ * @param gtxValue : target X geodata coordinate -+ * @param gtyValue : target Y geodata coordinate -+ * @param gtzValue : target Z geodata coordinate -+ * @param theight : target height (if instance of {@link Character}) -+ * @param instance -+ * @return {@code boolean} : True, when target can be seen. -+ */ -+ private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) -+ { -+ int goz = gozValue; -+ int gtz = gtzValue; -+ int gox = goxValue; -+ int goy = goyValue; -+ int gtx = gtxValue; -+ int gty = gtyValue; -+ -+ // get line of sight Z coordinates -+ double losoz = goz + ((oheight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ double lostz = gtz + ((theight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ -+ // get X delta and signum -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirox = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; -+ final byte dirtx = sx > 0 ? GeoStructure.CELL_FLAG_W : GeoStructure.CELL_FLAG_E; -+ -+ // get Y delta and signum -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte diroy = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ final byte dirty = sy > 0 ? GeoStructure.CELL_FLAG_N : GeoStructure.CELL_FLAG_S; -+ -+ // get Z delta -+ final int dm = Math.max(dx, dy); -+ final double dz = (lostz - losoz) / dm; -+ -+ // get direction flag for diagonal movement -+ final byte diroxy = getDirXY(dirox, diroy); -+ final byte dirtxy = getDirXY(dirtx, dirty); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte diro; -+ byte dirt; -+ -+ // initialize node values -+ int nox = gox; -+ int noy = goy; -+ int ntx = gtx; -+ int nty = gty; -+ byte nsweo = getNsweNearest(gox, goy, goz); -+ byte nswet = getNsweNearest(gtx, gty, gtz); -+ -+ // loop -+ ABlock block; -+ int index; -+ for (int i = 0; i < ((dm + 1) / 2); i++) -+ { -+ // reset direction flag -+ diro = 0; -+ dirt = 0; - -- if ((curX == prevX) && (curY == prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- continue; -+ // calculate next point XY coordinates -+ d -= dy; -+ d += dx; -+ nox += sx; -+ ntx -= sx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroxy; -+ dirt |= dirtxy; - } -+ else if (e2 > -dy) -+ { -+ // calculate next point X coordinate -+ d -= dy; -+ nox += sx; -+ ntx -= sx; -+ diro |= dirox; -+ dirt |= dirtx; -+ } -+ else if (e2 < dx) -+ { -+ // calculate next point Y coordinate -+ d += dx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroy; -+ dirt |= dirty; -+ } - -- final int beeCurZ = pointIter.z(); -- int curGeoZ = prevGeoZ; -- -- // Check if the position has geodata. -- if (hasGeoPos(curX, curY)) - { -- final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); -- curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); -- int maxHeight; -- if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) -+ // get block of the next cell -+ block = getBlock(nox, noy); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nsweo & diro) == 0) - { -- maxHeight = z + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); - } - else - { -- maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); - } - -- boolean canSeeThrough = false; -- if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) -+ // layer does not exist, return -+ if (index == -1) - { -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else -- { -- canSeeThrough = true; -- } -+ return false; - } - -- if (!canSeeThrough) -+ // get layer and next line of sight Z coordinate -+ goz = block.getHeight(index); -+ losoz += dz; -+ -+ // perform line of sight check, return when fails -+ if ((goz - losoz) > Config.MAX_OBSTACLE_HEIGHT) - { - return false; - } -+ -+ // get layer nswe -+ nsweo = block.getNswe(index); - } -+ { -+ // get block of the next cell -+ block = getBlock(ntx, nty); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nswet & dirt) == 0) -+ { -+ index = block.getIndexAbove(ntx, nty, gtz - GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ else -+ { -+ index = block.getIndexBelow(ntx, nty, gtz + GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ -+ // layer does not exist, return -+ if (index == -1) -+ { -+ return false; -+ } -+ -+ // get layer and next line of sight Z coordinate -+ gtz = block.getHeight(index); -+ lostz -= dz; -+ -+ // perform line of sight check, return when fails -+ if ((gtz - lostz) > Config.MAX_OBSTACLE_HEIGHT) -+ { -+ return false; -+ } -+ -+ // get layer nswe -+ nswet = block.getNswe(index); -+ } - -- prevX = curX; -- prevY = curY; -- prevGeoZ = curGeoZ; -- ++ptIndex; -+ // update coords -+ gox = nox; -+ goy = noy; -+ gtx = ntx; -+ gty = nty; - } - -- return true; -+ // when iteration is completed, compare final Z coordinates -+ return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); - } - - /** -- * Move check. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param zValue the z coordinate -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tzValue the target's z coordinate -- * @param instance the instance -- * @return the last Location (x,y,z) where player can walk - just before wall -+ * Check movement from coordinates to coordinates. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {code boolean} : True if target coordinates are reachable from origin coordinates - */ -- public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) -+ public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- final int geoX = getGeoX(x); -- final int geoY = getGeoY(y); -- final int z = getNearestZ(geoX, geoY, zValue); -- final int tGeoX = getGeoX(tx); -- final int tGeoY = getGeoY(ty); -- final int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } -- if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = z; -+ // perform geodata check -+ final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); -+ return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); -+ } -+ -+ /** -+ * Check movement from origin to target. Returns last available point in the checked path. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {@link Location} : Last point where object can walk (just before wall) -+ */ -+ public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) -+ { -+ return new Location(ox, oy, oz); -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) -+ { -+ return new Location(ox, oy, oz); -+ } - -- while (pointIter.next()) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -- -- if (hasGeoPos(prevX, prevY)) -- { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -- { -- // Can't move, return previous location. -- return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); -- } -- } -- -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return new Location(tx, ty, tz); - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != tz)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- // Different floors, return start location. -- return new Location(x, y, z); -+ return new Location(tx, ty, tz); - } - -- return new Location(tx, ty, tz); -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) -+ { -+ return new Location(tx, ty, tz); -+ } -+ -+ // perform geodata check -+ return checkMove(gox, goy, goz, gtx, gty, gtz, instance); - } - - /** -- * Checks if its possible to move from one location to another. -- * @param fromX the X coordinate to start checking from -- * @param fromY the Y coordinate to start checking from -- * @param fromZvalue the Z coordinate to start checking from -- * @param toX the X coordinate to end checking at -- * @param toY the Y coordinate to end checking at -- * @param toZvalue the Z coordinate to end checking at -- * @param instance the instance -- * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise -+ * With this method you can check if a position is visible or can be reached by beeline movement.
-+ * Target X and Y reachable and Z is on same floor: -+ *
    -+ *
  • Location of the target with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y reachable but Z is on another floor: -+ *
    -+ *
  • Location of the origin with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y not reachable: -+ *
    -+ *
  • Last accessible location in destination to target.
  • -+ *
-+ * @param gox : origin X geodata coordinate -+ * @param goy : origin Y geodata coordinate -+ * @param goz : origin Z geodata coordinate -+ * @param gtx : target X geodata coordinate -+ * @param gty : target Y geodata coordinate -+ * @param gtz : target Z geodata coordinate -+ * @param instance -+ * @return {@link GeoLocation} : The last allowed point of movement. - */ -- public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) -+ protected final GeoLocation checkMove(int gox, int goy, int goz, int gtx, int gty, int gtz, Instance instance) - { -- final int geoX = getGeoX(fromX); -- final int geoY = getGeoY(fromY); -- final int fromZ = getNearestZ(geoX, geoY, fromZvalue); -- final int tGeoX = getGeoX(toX); -- final int tGeoY = getGeoY(toY); -- final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); -- -- if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) -+ if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } -- if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) -+ if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = fromZ; -+ // get X delta, signum and direction flag -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirX = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; - -- while (pointIter.next()) -+ // get Y delta, signum and direction flag -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte dirY = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ -+ // get direction flag for diagonal movement -+ final byte dirXY = getDirXY(dirX, dirY); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte direction; -+ -+ // load pointer coordinates -+ int gpx = gox; -+ int gpy = goy; -+ int gpz = goz; -+ -+ // load next pointer -+ int nx = gpx; -+ int ny = gpy; -+ -+ // loop -+ int count = 0; -+ while (count++ < Config.MAX_ITERATIONS) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -+ direction = 0; - -- if (hasGeoPos(prevX, prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -+ d -= dy; -+ d += dx; -+ nx += sx; -+ ny += sy; -+ direction |= dirXY; -+ } -+ else if (e2 > -dy) -+ { -+ d -= dy; -+ nx += sx; -+ direction |= dirX; -+ } -+ else if (e2 < dx) -+ { -+ d += dx; -+ ny += sy; -+ direction |= dirY; -+ } -+ -+ // obstacle found, return -+ if ((getNsweNearest(gpx, gpy, gpz) & direction) == 0) -+ { -+ return new GeoLocation(gpx, gpy, gpz); -+ } -+ -+ // update pointer coordinates -+ gpx = nx; -+ gpy = ny; -+ gpz = getHeightNearest(nx, ny, gpz); -+ -+ // target coordinates reached -+ if ((gpx == gtx) && (gpy == gty)) -+ { -+ if (gpz == gtz) - { -- return false; -+ // path found, Z coordinates are okay, return target point -+ return new GeoLocation(gtx, gty, gtz); - } -+ -+ // path found, Z coordinates are not okay, return last good point -+ return new GeoLocation(gpx, gpy, gpz); - } -+ } -+ -+ return new GeoLocation(gox, goy, goz); -+ } -+ -+ /** -+ * Returns diagonal NSWE flag format of combined two NSWE flags. -+ * @param dirX : X direction NSWE flag -+ * @param dirY : Y direction NSWE flag -+ * @return byte : NSWE flag of combined direction -+ */ -+ private static byte getDirXY(byte dirX, byte dirY) -+ { -+ // check axis directions -+ if (dirY == GeoStructure.CELL_FLAG_N) -+ { -+ if (dirX == GeoStructure.CELL_FLAG_W) -+ { -+ return GeoStructure.CELL_FLAG_NW; -+ } - -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return GeoStructure.CELL_FLAG_NE; - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) -+ if (dirX == GeoStructure.CELL_FLAG_W) - { -- // Different floors. -- return false; -+ return GeoStructure.CELL_FLAG_SW; - } - -- return true; -+ return GeoStructure.CELL_FLAG_SE; - } - -+ /** -+ * Returns the list of location objects as a result of complete path calculation. -+ * @param ox : origin x -+ * @param oy : origin y -+ * @param oz : origin z -+ * @param tx : target x -+ * @param ty : target y -+ * @param tz : target z -+ * @param instance -+ * @return {@code List} : complete path from nodes -+ */ -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ return null; -+ } -+ -+ /** -+ * Returns the instance of the {@link GeoEngine}. -+ * @return {@link GeoEngine} : The instance. -+ */ - public static GeoEngine getInstance() - { - return SingletonHolder.INSTANCE; -@@ -743,6 +934,6 @@ - - private static class SingletonHolder - { -- protected static final GeoEngine INSTANCE = new GeoEngine(); -+ protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); - } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (working copy) -@@ -20,88 +20,84 @@ - import java.util.LinkedList; - import java.util.List; - import java.util.ListIterator; --import java.util.logging.Level; --import java.util.logging.Logger; - - import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; --import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; --import org.l2jmobius.gameserver.model.World; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -+import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.instancezone.Instance; - - /** -- * @author -Nemesiss- -+ * @author Hasha - */ --public class GeoEnginePathfinding -+final class GeoEnginePathfinding extends GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); -+ // pre-allocated buffers -+ private final BufferHolder[] _buffers; - -- private BufferInfo[] _buffers; -- - protected GeoEnginePathfinding() - { -- try -+ super(); -+ -+ final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ _buffers = new BufferHolder[array.length]; -+ int count = 0; -+ for (int i = 0; i < array.length; i++) - { -- final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ final String buf = array[i]; -+ final String[] args = buf.split("x"); - -- _buffers = new BufferInfo[array.length]; -- -- String buf; -- String[] args; -- for (int i = 0; i < array.length; i++) -+ try - { -- buf = array[i]; -- args = buf.split("x"); -- if (args.length != 2) -- { -- throw new Exception("Invalid buffer definition: " + buf); -- } -- -- _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); -+ final int size = Integer.parseInt(args[1]); -+ count += size; -+ _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); - } -+ catch (Exception e) -+ { -+ LOGGER.warning("GeoEnginePathfinding: Can not load buffer setting: " + buf); -+ } - } -- catch (Exception e) -- { -- LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); -- throw new Error("CellPathFinding: load aborted"); -- } -+ -+ LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); - } - -- public boolean pathNodesExist(short regionoffset) -+ @Override -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- return false; -- } -- -- public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) -- { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -- if (!GeoEngine.getInstance().hasGeo(x, y)) -+ // get origin and check existing geo coords -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { - 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)) -+ -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coords -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { - 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)))); -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // Prepare buffer for pathfinding calculations -+ final NodeBuffer buffer = getBuffer(64 + (2 * Math.max(Math.abs(gox - gtx), Math.abs(goy - gty)))); - if (buffer == null) - { - return null; - } - -- List path = null; -+ // find path -+ List path = null; - try - { -- final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); -- -+ final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); - if (result == null) - { - return null; -@@ -125,33 +121,45 @@ - return path; - } - -- int currentX, currentY, currentZ; -- ListIterator middlePoint; -+ // get path list iterator -+ final ListIterator point = path.listIterator(); - -- middlePoint = path.listIterator(); -- currentX = x; -- currentY = y; -- currentZ = z; -+ // get node A (origin) -+ int nodeAx = gox; -+ int nodeAy = goy; -+ short nodeAz = goz; - -- while (middlePoint.hasNext()) -+ // get node B -+ GeoLocation nodeB = (GeoLocation) point.next(); -+ -+ // iterate thought the path to optimize it -+ int count = 0; -+ while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) - { -- final AbstractNodeLoc locMiddle = middlePoint.next(); -- if (!middlePoint.hasNext()) -- { -- break; -- } -+ // get node C -+ final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - -- final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); -- if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) -+ // check movement from node A to node C -+ final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); -+ if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) - { -- middlePoint.remove(); -+ // can move from node A to node C -+ -+ // remove node B -+ point.remove(); - } - else - { -- currentX = locMiddle.getX(); -- currentY = locMiddle.getY(); -- currentZ = locMiddle.getZ(); -+ // can not move from node A to node C -+ -+ // set node A (node B is part of path, update A coordinates) -+ nodeAx = nodeB.getGeoX(); -+ nodeAy = nodeB.getGeoY(); -+ nodeAz = (short) nodeB.getZ(); - } -+ -+ // set node B -+ nodeB = (GeoLocation) point.next(); - } - - return path; -@@ -158,144 +166,102 @@ - } - - /** -- * Convert geodata position to pathnode position -- * @param geo_pos -- * @return pathnode position -+ * Create list of node locations as result of calculated buffer node tree. -+ * @param node : the entry point -+ * @return List : list of node location - */ -- public short getNodePos(int geo_pos) -+ private static List constructPath(Node node) - { -- return (short) (geo_pos >> 3); // OK? -- } -- -- /** -- * Convert node position to pathnode block position -- * @param node_pos -- * @return pathnode block position (0...255) -- */ -- public short getNodeBlock(int node_pos) -- { -- return (short) (node_pos % 256); -- } -- -- public byte getRegionX(int node_pos) -- { -- return (byte) ((node_pos >> 8) + World.TILE_X_MIN); -- } -- -- public byte getRegionY(int node_pos) -- { -- return (byte) ((node_pos >> 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 node_x rx -- * @return -- */ -- public int calculateWorldX(short node_x) -- { -- return World.MAP_MIN_X + (node_x * 128) + 48; -- } -- -- /** -- * Convert pathnode y to World y position -- * @param node_y -- * @return -- */ -- public int calculateWorldY(short node_y) -- { -- return World.MAP_MIN_Y + (node_y * 128) + 48; -- } -- -- private List constructPath(AbstractNode nodeValue) -- { -- final LinkedList path = new LinkedList<>(); -- int previousDirectionX = Integer.MIN_VALUE; -- int previousDirectionY = Integer.MIN_VALUE; -- int directionX, directionY; -+ // create empty list -+ final LinkedList list = new LinkedList<>(); - -- AbstractNode node = nodeValue; -- while (node.getParent() != null) -+ // set direction X/Y -+ int dx = 0; -+ int dy = 0; -+ -+ // get target parent -+ Node target = node; -+ Node parent = target.getParent(); -+ -+ // while parent exists -+ int count = 0; -+ while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { -- directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); -- directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); -+ // get parent <> target direction X/Y -+ final int nx = parent.getLoc().getGeoX() - target.getLoc().getGeoX(); -+ final int ny = parent.getLoc().getGeoY() - target.getLoc().getGeoY(); - -- // only add a new route point if moving direction changes -- if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) -+ // direction has changed? -+ if ((dx != nx) || (dy != ny)) - { -- previousDirectionX = directionX; -- previousDirectionY = directionY; -+ // add node to the beginning of the list -+ list.addFirst(target.getLoc()); - -- path.addFirst(node.getLoc()); -- node.setLoc(null); -+ // update direction X/Y -+ dx = nx; -+ dy = ny; - } - -- node = node.getParent(); -+ // move to next node, set target and get its parent -+ target = parent; -+ parent = target.getParent(); - } - -- return path; -+ // return list -+ return list; - } - -- private CellNodeBuffer alloc(int size) -+ /** -+ * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer. -+ * @param size : pre-calculated minimal required size -+ * @return NodeBuffer : buffer -+ */ -+ private final NodeBuffer getBuffer(int size) - { -- CellNodeBuffer current = null; -- for (BufferInfo i : _buffers) -+ NodeBuffer current = null; -+ for (BufferHolder holder : _buffers) - { -- if (i.mapSize >= size) -+ // Find proper size of buffer -+ if (holder._size < size) - { -- for (CellNodeBuffer buf : i.buffer) -+ continue; -+ } -+ -+ // Find unlocked NodeBuffer -+ for (NodeBuffer buffer : holder._buffer) -+ { -+ if (!buffer.isLocked()) - { -- if (buf.lock()) -- { -- current = buf; -- break; -- } -+ continue; - } -- if (current != null) -- { -- break; -- } - -- // not found, allocate temporary buffer -- current = new CellNodeBuffer(i.mapSize); -- current.lock(); -- if (i.buffer.size() < i.count) -- { -- i.buffer.add(current); -- break; -- } -+ return buffer; - } -+ -+ // NodeBuffer not found, allocate temporary buffer -+ current = new NodeBuffer(holder._size); -+ current.isLocked(); - } - - return current; - } - -- private static final class BufferInfo -+ /** -+ * NodeBuffer container with specified size and count of separate buffers. -+ */ -+ private static final class BufferHolder - { -- final int mapSize; -- final int count; -- ArrayList buffer; -+ final int _size; -+ List _buffer; - -- public BufferInfo(int size, int cnt) -+ public BufferHolder(int size, int count) - { -- mapSize = size; -- count = cnt; -- buffer = new ArrayList<>(count); -+ _size = size; -+ _buffer = new ArrayList<>(count); -+ for (int i = 0; i < count; i++) -+ { -+ _buffer.add(new NodeBuffer(size)); -+ } - } - } -- -- public static GeoEnginePathfinding getInstance() -- { -- return SingletonHolder.INSTANCE; -- } -- -- private static class SingletonHolder -- { -- protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); -- } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (working copy) -@@ -0,0 +1,197 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @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 height of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getHeightNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first above given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, above given coordinates. -+ */ -+ public abstract short getHeightAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first below given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is closest to given coordinates. -+ * @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 the NSWE flag byte of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getNsweNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first above given coordinates. -+ * @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 getNsweAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first below given coordinates. -+ * @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 getNsweBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is closes layer to given coordinates. -+ * @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. -+ * @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 above given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is first layer below given coordinates. -+ * @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 index to data of the cell, which is first layer below given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexBelowOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeight(int index); -+ -+ /** -+ * Returns the height of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightOriginal(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNswe(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNsweOriginal(int index); -+ -+ /** -+ * Sets the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @param nswe : New NSWE flag byte. -+ */ -+ public abstract void setNswe(int index, byte nswe); -+ -+ /** -+ * Saves the block in L2D format to {@link BufferedOutputStream}. Used only for L2D geodata conversion. -+ * @param stream : The stream. -+ * @throws IOException : Can't save the block to steam. -+ */ -+ public abstract void saveBlock(BufferedOutputStream stream) throws IOException; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (working copy) -@@ -0,0 +1,252 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockComplex(ByteBuffer bb, GeoFormat format) -+ { -+ // initialize buffer -+ _buffer = new byte[GeoStructure.BLOCK_CELLS * 3]; -+ -+ // load data -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // 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); -+ } -+ else -+ { -+ // get nswe -+ final byte nswe = bb.get(); -+ _buffer[i * 3] = nswe; -+ -+ // get height -+ final short height = bb.getShort(); -+ _buffer[(i * 3) + 1] = (byte) (height & 0x00FF); -+ _buffer[(i * 3) + 2] = (byte) (height >> 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height > worldZ ? height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height < worldZ ? height : Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public 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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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 int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_COMPLEX_L2D); -+ -+ // write block data -+ stream.write(_buffer, 0, GeoStructure.BLOCK_CELLS * 3); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (working copy) -@@ -0,0 +1,176 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockFlat extends ABlock -+{ -+ protected final short _height; -+ protected byte _nswe; -+ -+ /** -+ * Creates FlatBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockFlat(ByteBuffer bb, GeoFormat format) -+ { -+ _height = bb.getShort(); -+ _nswe = format != GeoFormat.L2D ? 0x0F : (byte) (0xFF); -+ if (format == GeoFormat.L2OFF) -+ { -+ bb.getShort(); -+ } -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return true; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height > worldZ ? _height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height < worldZ ? _height : Short.MAX_VALUE; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height > worldZ ? _nswe : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height < worldZ ? _nswe : 0; -+ } -+ -+ @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return index -+ return _height < worldZ ? 0 : -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _nswe = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_FLAT_L2D); -+ -+ // write height -+ stream.write((byte) (_height & 0x00FF)); -+ stream.write((byte) (_height >> 8)); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (working copy) -@@ -0,0 +1,465 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+import java.nio.ByteOrder; -+import java.util.Arrays; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockMultilayer extends ABlock -+{ -+ private static final int MAX_LAYERS = Byte.MAX_VALUE; -+ -+ private static ByteBuffer _temp; -+ -+ /** -+ * Initializes the temporarily buffer. -+ */ -+ public static void initialize() -+ { -+ // initialize temporarily buffer and sorting mechanism -+ _temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3); -+ _temp.order(ByteOrder.LITTLE_ENDIAN); -+ } -+ -+ /** -+ * Releases temporarily buffer. -+ */ -+ public static void release() -+ { -+ _temp = null; -+ } -+ -+ protected byte[] _buffer; -+ -+ /** -+ * Implicit constructor for children class. -+ */ -+ protected BlockMultilayer() -+ { -+ _buffer = null; -+ } -+ -+ /** -+ * Creates MultilayerBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockMultilayer(ByteBuffer bb, GeoFormat format) -+ { -+ // 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 = format != GeoFormat.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++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // get data -+ final short data = bb.getShort(); -+ -+ // add nswe and height -+ _temp.put((byte) (data & 0x000F)); -+ _temp.putShort((short) ((short) (data & 0xFFF0) >> 1)); -+ } -+ else -+ { -+ // add nswe -+ _temp.put(bb.get()); -+ -+ // add height -+ _temp.putShort(bb.getShort()); -+ } -+ } -+ } -+ -+ // 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 height -+ if (height > worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, return minimum value -+ return Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 height -+ if (height < worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, return maximum value -+ return Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 nswe -+ if (height > worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 nswe -+ if (height < worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @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 first layer data (first from bottom) -+ byte layers = _buffer[index++]; -+ -+ // loop though all cell layers, find closest layer -+ 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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ // get height -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(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]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ // get nswe -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ // set nswe -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_MULTILAYER_L2D); -+ -+ // for each cell -+ int index = 0; -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ // write layers count -+ final byte layers = _buffer[index++]; -+ stream.write(layers); -+ -+ // write cell data -+ stream.write(_buffer, index, layers * 3); -+ -+ // move index to next cell -+ index += layers * 3; -+ } -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (working copy) -@@ -0,0 +1,150 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockNull extends ABlock -+{ -+ private final byte _nswe; -+ -+ public BlockNull() -+ { -+ _nswe = (byte) 0xFF; -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return false; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweBelow(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) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) -+ { -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (nonexistent) -@@ -1,48 +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 . -- */ --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() -- { -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (nonexistent) -@@ -1,77 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (nonexistent) -@@ -1,56 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (working copy) -@@ -0,0 +1,39 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public enum GeoFormat -+{ -+ L2J("%d_%d.l2j"), -+ L2OFF("%d_%d_conv.dat"), -+ L2D("%d_%d.l2d"); -+ -+ private final String _filename; -+ -+ private GeoFormat(String filename) -+ { -+ _filename = filename; -+ } -+ -+ public String getFilename() -+ { -+ return _filename; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (working copy) -@@ -0,0 +1,67 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.geoengine.GeoEngine; -+import org.l2jmobius.gameserver.model.Location; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoLocation extends Location -+{ -+ private byte _nswe; -+ -+ public GeoLocation(int x, int y, int z) -+ { -+ super(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public void set(int x, int y, short z) -+ { -+ super.setXYZ(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public int getGeoX() -+ { -+ return _x; -+ } -+ -+ public int getGeoY() -+ { -+ return _y; -+ } -+ -+ @Override -+ public int getX() -+ { -+ return GeoEngine.getWorldX(_x); -+ } -+ -+ @Override -+ public int getY() -+ { -+ return GeoEngine.getWorldY(_y); -+ } -+ -+ public byte getNSWE() -+ { -+ return _nswe; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (working copy) -@@ -0,0 +1,70 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoStructure -+{ -+ // cells -+ public static final byte CELL_FLAG_E = 1 << 0; -+ public static final byte CELL_FLAG_W = 1 << 1; -+ public static final byte CELL_FLAG_S = 1 << 2; -+ public static final byte CELL_FLAG_N = 1 << 3; -+ public static final byte CELL_FLAG_SE = 1 << 4; -+ public static final byte CELL_FLAG_SW = 1 << 5; -+ public static final byte CELL_FLAG_NE = 1 << 6; -+ public static final byte CELL_FLAG_NW = (byte) (1 << 7); -+ -+ public static final int CELL_HEIGHT = 8; -+ public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6; -+ -+ // blocks -+ public static final byte TYPE_FLAT_L2J_L2OFF = 0; -+ public static final byte TYPE_FLAT_L2D = (byte) 0xD0; -+ public static final byte TYPE_COMPLEX_L2J = 1; -+ public static final byte TYPE_COMPLEX_L2OFF = 0x40; -+ public static final byte TYPE_COMPLEX_L2D = (byte) 0xD1; -+ 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) -+ public static final byte TYPE_MULTILAYER_L2D = (byte) 0xD2; -+ -+ 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; -+ -+ // regions -+ 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; -+ -+ // global geodata -+ private static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); -+ private 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; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (nonexistent) -@@ -1,42 +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 . -- */ --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); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (working copy) -@@ -0,0 +1,53 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public interface IGeoObject -+{ -+ /** -+ * Returns geodata X coordinate of the {@link IGeoObject}. -+ * @return int : Geodata X coordinate. -+ */ -+ int getGeoX(); -+ -+ /** -+ * Returns geodata Y coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Y coordinate. -+ */ -+ int getGeoY(); -+ -+ /** -+ * Returns geodata Z coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Z coordinate. -+ */ -+ int getGeoZ(); -+ -+ /** -+ * Returns height of the {@link IGeoObject}. -+ * @return int : Height. -+ */ -+ int getHeight(); -+ -+ /** -+ * Returns {@link IGeoObject} data. -+ * @return byte[][] : {@link IGeoObject} data. -+ */ -+ byte[][] getObjectGeoData(); -+} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (nonexistent) -@@ -1,47 +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 . -- */ --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(); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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); -- -- // 1 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (nonexistent) -@@ -1,55 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --/** -- * @author HorridoJoho -- */ --public final class NullRegion implements IRegion --{ -- public static final NullRegion INSTANCE = new NullRegion(); -- -- @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 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Region.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (nonexistent) -@@ -1,92 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (nonexistent) -@@ -1,87 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public abstract class AbstractNode --{ -- private T _loc; -- private AbstractNode _parent; -- -- public AbstractNode(T loc) -- { -- _loc = loc; -- } -- -- public void setParent(AbstractNode p) -- { -- _parent = p; -- } -- -- public AbstractNode getParent() -- { -- return _parent; -- } -- -- public T getLoc() -- { -- return _loc; -- } -- -- public void setLoc(T l) -- { -- _loc = l; -- } -- -- @Override -- public int hashCode() -- { -- final int prime = 31; -- int result = 1; -- result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); -- return result; -- } -- -- @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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (nonexistent) -@@ -1,33 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --/** -- * @author -Nemesiss- -- */ --public abstract class AbstractNodeLoc --{ -- public abstract int getX(); -- -- public abstract int getY(); -- -- public abstract int getZ(); -- -- public abstract int getNodeX(); -- -- public abstract int getNodeY(); --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (nonexistent) -@@ -1,67 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public class CellNode extends AbstractNode --{ -- 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (nonexistent) -@@ -1,348 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --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 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) -- { -- _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(); -- } -- -- public final List debugPath() -- { -- final List 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 void getNeighbors() -- { -- if (!_current.getLoc().canGoAll()) -- { -- 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); -- } -- -- // SouthEast -- if ((nodeE != null) && (nodeS != null)) -- { -- if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) -- { -- addNode(x + 1, y + 1, z, true); -- } -- } -- -- // SouthWest -- if ((nodeS != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) -- { -- addNode(x - 1, y + 1, z, true); -- } -- } -- -- // NorthEast -- if ((nodeN != null) && (nodeE != null)) -- { -- if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) -- { -- addNode(x + 1, y - 1, z, true); -- } -- } -- -- // NorthWest -- if ((nodeN != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) -- { -- addNode(x - 1, y - 1, z, true); -- } -- } -- } -- -- private 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(); -- // reinit node if needed -- if (result.getLoc() != null) -- { -- result.getLoc().set(x, y, z); -- } -- else -- { -- result.setLoc(new NodeLoc(x, y, z)); -- } -- } -- -- return result; -- } -- -- private 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 boolean isHighWeight(int x, int y, int z) -- { -- final CellNode result = getNode(x, y, z); -- if (result == null) -- { -- return true; -- } -- -- if (!result.getLoc().canGoAll()) -- { -- return true; -- } -- if (Math.abs(result.getLoc().getZ() - z) > 16) -- { -- return true; -- } -- -- return false; -- } -- -- private 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (working copy) -@@ -0,0 +1,87 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+ -+/** -+ * @author Hasha -+ */ -+public class Node -+{ -+ // node coords and nswe flag -+ private GeoLocation _loc; -+ -+ // node parent (for reverse path construction) -+ private Node _parent; -+ // node child (for moving over nodes during iteration) -+ private Node _child; -+ -+ // node G cost (movement cost = parent movement cost + current movement cost) -+ private double _cost = -1000; -+ -+ public void setLoc(int x, int y, int z) -+ { -+ _loc = new GeoLocation(x, y, z); -+ } -+ -+ public GeoLocation getLoc() -+ { -+ return _loc; -+ } -+ -+ public void setParent(Node parent) -+ { -+ _parent = parent; -+ } -+ -+ public Node getParent() -+ { -+ return _parent; -+ } -+ -+ public void setChild(Node child) -+ { -+ _child = child; -+ } -+ -+ public Node getChild() -+ { -+ return _child; -+ } -+ -+ public void setCost(double cost) -+ { -+ _cost = cost; -+ } -+ -+ public double getCost() -+ { -+ return _cost; -+ } -+ -+ public void free() -+ { -+ // reset node location -+ _loc = null; -+ -+ // reset node parent, child and cost -+ _parent = null; -+ _child = null; -+ _cost = -1000; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (working copy) -@@ -0,0 +1,306 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import java.util.concurrent.locks.ReentrantLock; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+ -+/** -+ * @author DS, Hasha; Credits to Diamond -+ */ -+public class NodeBuffer -+{ -+ private final ReentrantLock _lock = new ReentrantLock(); -+ private final int _size; -+ private final Node[][] _buffer; -+ -+ // center coordinates -+ private int _cx = 0; -+ private int _cy = 0; -+ -+ // target coordinates -+ private int _gtx = 0; -+ private int _gty = 0; -+ private short _gtz = 0; -+ -+ private Node _current = null; -+ -+ /** -+ * Constructor of NodeBuffer. -+ * @param size : one dimension size of buffer -+ */ -+ public NodeBuffer(int size) -+ { -+ // set size -+ _size = size; -+ -+ // initialize buffer -+ _buffer = new Node[size][size]; -+ for (int x = 0; x < size; x++) -+ { -+ for (int y = 0; y < size; y++) -+ { -+ _buffer[x][y] = 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 Node : first node of path -+ */ -+ public Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) -+ { -+ // set coordinates (middle of the line (gox,goy) - (gtx,gty), will be in the center of the buffer) -+ _cx = gox + ((gtx - gox - _size) / 2); -+ _cy = goy + ((gty - goy - _size) / 2); -+ -+ _gtx = gtx; -+ _gty = gty; -+ _gtz = gtz; -+ -+ _current = getNode(gox, goy, goz); -+ _current.setCost(getCostH(gox, goy, goz)); -+ -+ int count = 0; -+ do -+ { -+ // reached target? -+ if ((_current.getLoc().getGeoX() == _gtx) && (_current.getLoc().getGeoY() == _gty) && (Math.abs(_current.getLoc().getZ() - _gtz) < 8)) -+ { -+ return _current; -+ } -+ -+ // expand current node -+ expand(); -+ -+ // move pointer -+ _current = _current.getChild(); -+ } -+ while ((_current != null) && (count++ < Config.MAX_ITERATIONS)); -+ -+ return null; -+ } -+ -+ public boolean isLocked() -+ { -+ return _lock.tryLock(); -+ } -+ -+ public void free() -+ { -+ _current = null; -+ -+ for (Node[] nodes : _buffer) -+ { -+ for (Node node : nodes) -+ { -+ if (node.getLoc() != null) -+ { -+ node.free(); -+ } -+ } -+ } -+ -+ _lock.unlock(); -+ } -+ -+ /** -+ * Check _current Node and add its neighbors to the buffer. -+ */ -+ private final void expand() -+ { -+ // can't move anywhere, don't expand -+ final byte nswe = _current.getLoc().getNSWE(); -+ if (nswe == 0) -+ { -+ return; -+ } -+ -+ // get geo coords of the node to be expanded -+ final int x = _current.getLoc().getGeoX(); -+ final int y = _current.getLoc().getGeoY(); -+ final short z = (short) _current.getLoc().getZ(); -+ -+ // can move north, expand -+ if ((nswe & GeoStructure.CELL_FLAG_N) != 0) -+ { -+ addNode(x, y - 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move south, expand -+ if ((nswe & GeoStructure.CELL_FLAG_S) != 0) -+ { -+ addNode(x, y + 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_W) != 0) -+ { -+ addNode(x - 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_E) != 0) -+ { -+ addNode(x + 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move north-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NW) != 0) -+ { -+ addNode(x - 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move north-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NE) != 0) -+ { -+ addNode(x + 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SW) != 0) -+ { -+ addNode(x - 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SE) != 0) -+ { -+ addNode(x + 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ } -+ -+ /** -+ * Returns node, if it exists in buffer. -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param z : node Z coord -+ * @return Node : node, if exits in buffer -+ */ -+ private final Node getNode(int x, int y, short z) -+ { -+ // check node X out of coordinates -+ final int ix = x - _cx; -+ if ((ix < 0) || (ix >= _size)) -+ { -+ return null; -+ } -+ -+ // check node Y out of coordinates -+ final int iy = y - _cy; -+ if ((iy < 0) || (iy >= _size)) -+ { -+ return null; -+ } -+ -+ // get node -+ final Node result = _buffer[ix][iy]; -+ -+ // check and update -+ if (result.getLoc() == null) -+ { -+ result.setLoc(x, y, z); -+ } -+ -+ // return node -+ return result; -+ } -+ -+ /** -+ * Add node given by coordinates to the buffer. -+ * @param x : geo X coord -+ * @param y : geo Y coord -+ * @param z : geo Z coord -+ * @param weight : weight of movement to new node -+ */ -+ private final void addNode(int x, int y, short z, int weight) -+ { -+ // get node to be expanded -+ final Node node = getNode(x, y, z); -+ if (node == null) -+ { -+ return; -+ } -+ -+ // Z distance between nearby cells is higher than cell size -+ if (node.getLoc().getZ() > (z + (2 * GeoStructure.CELL_HEIGHT))) -+ { -+ return; -+ } -+ -+ // node was already expanded, return -+ if (node.getCost() >= 0) -+ { -+ return; -+ } -+ -+ node.setParent(_current); -+ if (node.getLoc().getNSWE() != (byte) 0xFF) -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + (weight * Config.OBSTACLE_MULTIPLIER)); -+ } -+ else -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + weight); -+ } -+ -+ Node current = _current; -+ int count = 0; -+ while ((current.getChild() != null) && (count < (Config.MAX_ITERATIONS * 4))) -+ { -+ count++; -+ if (current.getChild().getCost() > node.getCost()) -+ { -+ node.setChild(current.getChild()); -+ break; -+ } -+ current = current.getChild(); -+ } -+ -+ if (count >= (Config.MAX_ITERATIONS * 4)) -+ { -+ System.err.println("Pathfinding: too long loop detected, cost:" + node.getCost()); -+ } -+ -+ current.setChild(node); -+ } -+ -+ /** -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param i : node Z coord -+ * @return double : node cost -+ */ -+ private final double getCostH(int x, int y, int i) -+ { -+ final int dX = x - _gtx; -+ final int dY = y - _gty; -+ final int dZ = (i - _gtz) / GeoStructure.CELL_HEIGHT; -+ -+ // return (Math.abs(dX) + Math.abs(dY) + Math.abs(dZ)) * Config.HEURISTIC_WEIGHT; // Manhattan distance -+ return Math.sqrt((dX * dX) + (dY * dY) + (dZ * dZ)) * Config.HEURISTIC_WEIGHT; // Direct distance -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -- --/** -- * @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 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 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; -- // return super.hashCode(); -- } -- -- @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; -- if (_x != other._x) -- { -- return false; -- } -- if (_y != other._y) -- { -- return false; -- } -- if (_goNorth != other._goNorth) -- { -- return false; -- } -- if (_goEast != other._goEast) -- { -- return false; -- } -- if (_goSouth != other._goSouth) -- { -- return false; -- } -- if (_goWest != other._goWest) -- { -- return false; -- } -- if (_geoHeight != other._geoHeight) -- { -- return false; -- } -- return true; -- } --} -Index: java/org/l2jmobius/gameserver/model/actor/Creature.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/Creature.java (revision 8428) -+++ java/org/l2jmobius/gameserver/model/actor/Creature.java (working copy) -@@ -65,8 +65,6 @@ - import org.l2jmobius.gameserver.enums.TeleportWhereType; - import org.l2jmobius.gameserver.enums.UserInfoType; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; - import org.l2jmobius.gameserver.instancemanager.IdManager; - import org.l2jmobius.gameserver.instancemanager.MapRegionManager; - import org.l2jmobius.gameserver.instancemanager.QuestManager; -@@ -2566,7 +2564,7 @@ - - public boolean disregardingGeodata; - public int onGeodataPathIndex; -- public List geoPath; -+ public List geoPath; - public int geoPathAccurateTx; - public int geoPathAccurateTy; - public int geoPathGtx; -@@ -3443,7 +3441,7 @@ - if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) - { - // Path calculation -- overrides previous movement check -- m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); -+ m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); - if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found - { - if (isPlayer() && !_isFlying && !isInWater) -Index: java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (revision 8424) -+++ java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (working copy) -@@ -12746,7 +12746,7 @@ - { - if (Config.CORRECT_PLAYER_Z) - { -- final int nearestZ = GeoEngine.getInstance().getHigherHeight(getX(), getY(), getZ()); -+ final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); - if (getZ() < nearestZ) - { - teleToLocation(new Location(getX(), getY(), nearestZ)); -Index: java/org/l2jmobius/gameserver/util/GeoUtils.java -=================================================================== ---- java/org/l2jmobius/gameserver/util/GeoUtils.java (revision 8409) -+++ java/org/l2jmobius/gameserver/util/GeoUtils.java (working copy) -@@ -19,7 +19,7 @@ - import java.awt.Color; - - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; - import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; - -@@ -26,25 +26,25 @@ - /** - * @author HorridoJoho - */ --public final class GeoUtils -+public class GeoUtils - { - public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); - - final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); - - while (iter.next()) - { -- final int wx = GeoEngine.getInstance().getWorldX(iter.x()); -- final int wy = GeoEngine.getInstance().getWorldY(iter.y()); -+ final int wx = GeoEngine.getWorldX(iter.x()); -+ final int wy = GeoEngine.getWorldY(iter.y()); - - prim.addPoint(Color.RED, wx, wy, z); - } -@@ -53,21 +53,21 @@ - - public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); -- int wy = GeoEngine.getInstance().getWorldY(prevY); -+ int wx = GeoEngine.getWorldX(prevX); -+ int wy = GeoEngine.getWorldY(prevY); - int wz = iter.z(); - prim.addPoint(Color.RED, wx, wy, wz); - -@@ -78,8 +78,8 @@ - - if ((curX != prevX) || (curY != prevY)) - { -- wx = GeoEngine.getInstance().getWorldX(curX); -- wy = GeoEngine.getInstance().getWorldY(curY); -+ wx = GeoEngine.getWorldX(curX); -+ wy = GeoEngine.getWorldY(curY); - wz = iter.z(); - - prim.addPoint(Color.RED, wx, wy, wz); -@@ -93,7 +93,7 @@ - - private static Color getDirectionColor(int x, int y, int z, int nswe) - { -- if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) -+ if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) - { - return Color.GREEN; - } -@@ -109,9 +109,8 @@ - int iPacket = 0; - - ExServerPrimitive exsp = null; -- final GeoEngine ge = GeoEngine.getInstance(); -- final int playerGx = ge.getGeoX(player.getX()); -- final int playerGy = ge.getGeoY(player.getY()); -+ final int playerGx = GeoEngine.getGeoX(player.getX()); -+ final int playerGy = GeoEngine.getGeoY(player.getY()); - for (int dx = -geoRadius; dx <= geoRadius; ++dx) - { - for (int dy = -geoRadius; dy <= geoRadius; ++dy) -@@ -135,12 +134,12 @@ - final int gx = playerGx + dx; - final int gy = playerGy + dy; - -- final int x = ge.getWorldX(gx); -- final int y = ge.getWorldY(gy); -- final int z = ge.getNearestZ(gx, gy, player.getZ()); -+ final int x = GeoEngine.getWorldX(gx); -+ final int y = GeoEngine.getWorldY(gy); -+ final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); - - // north arrow -- Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); -+ Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); - 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); -@@ -147,7 +146,7 @@ - exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z); - - // east arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); - 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); -@@ -154,13 +153,13 @@ - exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z); - - // south arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); - 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, Cell.NSWE_WEST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); - 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); -@@ -188,15 +187,15 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_EAST; -+ return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_EAST; -+ return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; - } - else - { -- return Cell.NSWE_EAST; -+ return GeoStructure.CELL_FLAG_E; // Direction.EAST; - } - } - else if (x < lastX) // west -@@ -203,26 +202,27 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_WEST; -+ return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_WEST; -+ return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; - } - else - { -- return Cell.NSWE_WEST; -+ return GeoStructure.CELL_FLAG_W; // Direction.WEST; - } - } -- else // unchanged x -+ else -+ // unchanged x - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH; -+ return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH; -+ return GeoStructure.CELL_FLAG_N; // Direction.NORTH; - } - else - { -Index: java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java -=================================================================== ---- java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (nonexistent) -+++ java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (working copy) -@@ -0,0 +1,386 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.tools.geodataconverter; -+ -+import java.io.BufferedOutputStream; -+import java.io.File; -+import java.io.FileOutputStream; -+import java.io.RandomAccessFile; -+import java.nio.ByteOrder; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.Scanner; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.commons.util.PropertiesParser; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoDataConverter -+{ -+ private static GeoFormat _format; -+ private static ABlock[][] _blocks; -+ -+ public static void main(String[] args) -+ { -+ // initialize config -+ loadGeoengineConfigs(); -+ -+ // get geodata format -+ String type = ""; -+ try (Scanner scn = new Scanner(System.in)) -+ { -+ while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) -+ { -+ System.out.println("GeoDataConverter: Select source geodata type:"); -+ System.out.println(" J: L2J (e.g. 23_22.l2j)"); -+ System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); -+ System.out.println(" E: Exit"); -+ System.out.print("Choice: "); -+ type = scn.next(); -+ } -+ } -+ if (type.equalsIgnoreCase("E")) -+ { -+ System.exit(0); -+ } -+ -+ _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; -+ -+ // start conversion -+ System.out.println("GeoDataConverter: Converting all " + _format + " files."); -+ -+ // initialize geodata container -+ _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files -+ int converted = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) -+ { -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) -+ { -+ final String input = String.format(_format.getFilename(), rx, ry); -+ final String filepath = Config.GEODATA_PATH; -+ final File f = new File(filepath + input); -+ if (f.exists() && !f.isDirectory()) -+ { -+ // load geodata -+ if (!loadGeoBlocks(input)) -+ { -+ System.out.println("GeoDataConverter: Unable to load " + input + " region file."); -+ continue; -+ } -+ -+ // recalculate nswe -+ if (!recalculateNswe()) -+ { -+ System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); -+ continue; -+ } -+ -+ // save geodata -+ final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); -+ if (!saveGeoBlocks(output)) -+ { -+ System.out.println("GeoDataConverter: Unable to save " + output + " region file."); -+ continue; -+ } -+ -+ converted++; -+ System.out.println("GeoDataConverter: Created " + output + " region file."); -+ } -+ } -+ } -+ System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); -+ } -+ -+ /** -+ * Loads geo blocks from buffer of the region file. -+ * @param filename : The name of the to load. -+ * @return boolean : True when successful. -+ */ -+ private static boolean loadGeoBlocks(String filename) -+ { -+ // region file is load-able, try to load it -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) -+ { -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) -+ if (_format == GeoFormat.L2OFF) -+ { -+ for (int i = 0; i < 18; i++) -+ { -+ buffer.get(); -+ } -+ } -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ if (_format == GeoFormat.L2J) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2J: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2J: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ else -+ { -+ // get block type -+ final short type = buffer.getShort(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ default: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ if (buffer.remaining() > 0) -+ { -+ System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ return false; -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); -+ return false; -+ } -+ } -+ -+ /** -+ * Recalculate diagonal flags for the region file. -+ * @return boolean : True when successful. -+ */ -+ private static boolean recalculateNswe() -+ { -+ try -+ { -+ for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) -+ { -+ for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) -+ { -+ // get block -+ final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // skip flat blocks -+ if (block instanceof BlockFlat) -+ { -+ continue; -+ } -+ -+ // for complex and multilayer blocks go though all layers -+ short height = Short.MAX_VALUE; -+ int index; -+ while ((index = block.getIndexBelow(x, y, height)) != -1) -+ { -+ // get height and nswe -+ height = block.getHeight(index); -+ byte nswe = block.getNswe(index); -+ -+ // update nswe with diagonal flags -+ nswe = updateNsweBelow(x, y, height, nswe); -+ -+ // set nswe of the cell -+ block.setNswe(index, nswe); -+ } -+ } -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ /** -+ * Updates the NSWE flag with diagonal flags. -+ * @param x : Geodata X coordinate. -+ * @param y : Geodata Y coordinate. -+ * @param z : Geodata Z coordinate. -+ * @param nsweValue : NSWE flag to be updated. -+ * @return byte : Updated NSWE flag. -+ */ -+ private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) -+ { -+ byte nswe = nsweValue; -+ -+ // calculate virtual layer height -+ final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); -+ -+ // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) -+ final byte nsweN = getNsweBelow(x, y - 1, height); -+ final byte nsweS = getNsweBelow(x, y + 1, height); -+ final byte nsweW = getNsweBelow(x - 1, y, height); -+ final byte nsweE = getNsweBelow(x + 1, y, height); -+ -+ // north-west -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NW; -+ } -+ -+ // north-east -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NE; -+ } -+ -+ // south-west -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SW; -+ } -+ -+ // south-east -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SE; -+ } -+ -+ return nswe; -+ } -+ -+ private static byte getNsweBelow(int geoX, int geoY, short worldZ) -+ { -+ // out of geo coordinates -+ if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) -+ { -+ return 0; -+ } -+ -+ // out of geo coordinates -+ if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) -+ { -+ return 0; -+ } -+ -+ // get block -+ final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // get index, when valid, return nswe -+ final int index = block.getIndexBelow(geoX, geoY, worldZ); -+ return index == -1 ? 0 : block.getNswe(index); -+ } -+ -+ /** -+ * Save region file to file. -+ * @param filename : The name of file to save. -+ * @return boolean : True when successful. -+ */ -+ private static boolean saveGeoBlocks(String filename) -+ { -+ try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) -+ { -+ // loop over region blocks and save each block -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[ix][iy].saveBlock(bos); -+ } -+ } -+ -+ // flush data to file -+ bos.flush(); -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ private static void loadGeoengineConfigs() -+ { -+ final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); -+ Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); -+ Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); -+ Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); -+ Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); -+ Config.PATHFINDING = geoData.getBoolean("PathFinding", true); -+ Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -+ Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); -+ Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); -+ Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); -+ Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); -+ Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); -+ } -+} -\ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/geodata/Readme.txt b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/geodata/Readme.txt index a480c8c380..2611882e56 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/geodata/Readme.txt +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/geodata/Readme.txt @@ -23,8 +23,8 @@ a - Prerequisites b - Make it work ---------------------------------------------- -To make geodata working: -* unpack your geodata files into "/data/geodata" folder -* open "/config/GeoEngine.ini" with your favorite text editor and then edit following config: - - CoordSynchronize = 2 -* If you do not use any geodata files, the server will automatically change this setting to -1. +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. diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java index 60cfac5c02..63f6ac589c 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java @@ -295,7 +295,7 @@ public class FourSepulchers extends AbstractNpcAI implements IXmlReader { if ((npc != null) && !npc.isDead()) { - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), npc.getSpawn().getLocation().getX() + getRandom(-400, 400), npc.getSpawn().getLocation().getY() + getRandom(-400, 400), npc.getZ(), npc.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), npc.getSpawn().getLocation().getX() + getRandom(-400, 400), npc.getSpawn().getLocation().getY() + getRandom(-400, 400), npc.getZ(), npc.getInstanceWorld()); if (Util.calculateDistance(npc, npc.getSpawn().getLocation(), false, false) < 600) { npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java index 941690319d..1d1149d6b2 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java @@ -78,7 +78,7 @@ public class BoyAndGirl extends AbstractNpcAI startQuestTimer("NPC_SHOUT", 10000 + (getRandom(5) * 1000), npc, null); npc.setRunning(); final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 200, 600); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); return super.onSpawn(npc); } @@ -86,7 +86,7 @@ public class BoyAndGirl extends AbstractNpcAI public void onMoveFinished(Npc npc) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 200, 600); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); super.onMoveFinished(npc); } diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java index 558f2631e6..3bcf3810cb 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java @@ -48,7 +48,7 @@ public class Devno extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java index 7525cc99e8..ce81f5c05c 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java @@ -48,7 +48,7 @@ public class Eleve extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java index 7d9150b156..f4e942c326 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java @@ -46,7 +46,7 @@ public class Handermonkey extends AbstractNpcAI { final int x = npc.getSpawn().getX() + (getRandom(-100, 100)); final int y = npc.getSpawn().getY() + (getRandom(-100, 100)); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x, y, npc.getZ(), npc.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x, y, npc.getZ(), npc.getInstanceWorld()); addMoveToDesire(npc, loc, 0); } else diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java index c993ce8936..7df61da041 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java @@ -48,7 +48,7 @@ public class Karonf extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java index 6e8cbee920..589d376f6b 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java @@ -48,7 +48,7 @@ public class Marsha extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java index c616abccc6..5c09d531c8 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java @@ -48,7 +48,7 @@ public class Morgan extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java index 3652b4ab02..fe20952121 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java @@ -48,7 +48,7 @@ public class Rubentis extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", 10000 + (getRandom(5) * 1000), npc, null); } diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java index beece64521..b0944f763d 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java @@ -63,7 +63,7 @@ public class Vortex extends AbstractNpcAI final int x = (int) (attackers.getX() + (600 * Math.cos(radians))); final int y = (int) (attackers.getY() + (600 * Math.sin(radians))); final int z = attackers.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(attackers.getX(), attackers.getY(), attackers.getZ(), x, y, z, attackers.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(attackers.getX(), attackers.getY(), attackers.getZ(), x, y, z, attackers.getInstanceWorld()); attackers.broadcastPacket(new FlyToLocation(attackers, x, y, z, FlyToLocation.FlyType.THROW_UP, 800, 800, 800)); attackers.setXYZ(loc); attackers.broadcastPacket(new ValidateLocation(attackers)); diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/others/FleeMonsters.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/others/FleeMonsters.java index 9346aebc95..b5de804b28 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/others/FleeMonsters.java +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/ai/others/FleeMonsters.java @@ -68,7 +68,7 @@ public class FleeMonsters extends AbstractNpcAI final int posX = (int) (npc.getX() + (FLEE_DISTANCE * Math.cos(radians))); final int posY = (int) (npc.getY() + (FLEE_DISTANCE * Math.sin(radians))); final int posZ = npc.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, attacker.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); return super.onAttack(npc, attacker, damage, isSummon); } diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index 524652cbc1..18511c6541 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { @@ -133,8 +133,8 @@ public class AdminGeodata implements IAdminCommandHandler } case "admin_geomap": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y + " (" + ((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + "," + ((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + " to " + ((((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + "," + ((((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + ")"); break; } diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java index f3dffa1227..76572f7f25 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java @@ -57,8 +57,8 @@ public class AdminMissingHtmls implements IAdminCommandHandler { case "admin_geomap_missing_htmls": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final int topLeftX = (x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE; final int topLeftY = (y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE; final int bottomRightX = (((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1; diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index fdb2319138..a0a34f1051 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -19,9 +19,9 @@ package handlers.admincommandhandlers; import java.util.List; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; +import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.util.BuilderUtil; @@ -44,13 +44,13 @@ public class AdminPathNode implements IAdminCommandHandler } if (activeChar.getTarget() != null) { - final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { BuilderUtil.sendSysMessage(activeChar, "No Route!"); return true; } - for (AbstractNodeLoc a : path) + for (Location a : path) { BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/Blink.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/Blink.java index 22f2c84eae..22697e8e20 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/Blink.java +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/Blink.java @@ -88,7 +88,7 @@ public class Blink extends AbstractEffect final int y = effected.getY() + y1; final int z = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, destination, _flyType, _flySpeed, _flyDelay, _animationSpeed)); diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/Fear.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/Fear.java index 95a5afdf98..88c795e04b 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/Fear.java +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/Fear.java @@ -100,7 +100,7 @@ public class Fear extends AbstractEffect final int posY = (int) (effected.getY() + (FEAR_RANGE * Math.sin(radians))); final int posZ = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); } } diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java index fddd94c60a..55c2e78d8a 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java @@ -57,7 +57,7 @@ public class FlyAway extends AbstractEffect final int y = (int) (effector.getY() - (nRadius * (dy / distance))); final int z = effector.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.broadcastPacket(new FlyToLocation(effected, destination, FlyType.THROW_UP)); effected.setXYZ(destination); diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java index 3c3f9aca62..14809ac499 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java @@ -179,7 +179,7 @@ public class KnockBack extends AbstractEffect final int x = (int) (effected.getX() + (_distance * Math.cos(radians))); final int y = (int) (effected.getY() + (_distance * Math.sin(radians))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, loc, _type, _speed, _delay, _animationSpeed)); diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/PullBack.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/PullBack.java index ed6a37eff6..38fe2fe9d0 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/PullBack.java +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/PullBack.java @@ -73,7 +73,7 @@ public class PullBack extends AbstractEffect } // In retail, you get debuff, but you are not even moved if there is obstacle. You are still disabled from using skills and moving though. - if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effector.getInstanceWorld())) + if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effected.getInstanceWorld())) { effected.broadcastPacket(new FlyToLocation(effected, effector, _type, _speed, _delay, _animationSpeed)); effected.setXYZ(effector.getX(), effector.getY(), GeoEngine.getInstance().getHeight(effector.getX(), effector.getY(), effector.getZ()) + 10); diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java index d6f9664141..813e496c47 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java @@ -87,7 +87,7 @@ public class TeleportToSummon extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = summon.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java index d3c263978c..c9dc34ac22 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java @@ -76,7 +76,7 @@ public class TeleportToTarget extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java index f2148adb54..c7ca6f64cb 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java @@ -65,7 +65,7 @@ public class RollingDice implements IItemHandler final int x = player.getX() + x1; final int y = player.getY() + y1; final int z = player.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); Broadcast.toSelfAndKnownPlayers(player, new Dice(player.getObjectId(), itemId, number, destination.getX(), destination.getY(), destination.getZ())); final SystemMessage sm = new SystemMessage(SystemMessageId.C1_HAS_ROLLED_A_S2); diff --git a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/targethandlers/Ground.java b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/targethandlers/Ground.java index acce8f1b2f..1328ff01b5 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/targethandlers/Ground.java +++ b/L2J_Mobius_7.0_PreludeOfWar/dist/game/data/scripts/handlers/targethandlers/Ground.java @@ -52,7 +52,7 @@ public class Ground implements ITargetTypeHandler return null; } - if (!GeoEngine.getInstance().canSeeTarget(creature, worldPosition)) + if (!GeoEngine.getInstance().canSeeLocation(creature, worldPosition)) { if (sendMessage) { diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/Config.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/Config.java index 027534e34f..11aa8b3950 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/Config.java @@ -61,6 +61,7 @@ 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; @@ -967,12 +968,17 @@ public class Config // GeoEngine // -------------------------------------------------- public static Path GEODATA_PATH; + public static GeoType GEODATA_TYPE; public static boolean PATHFINDING; public static String PATHFIND_BUFFERS; - public static float LOW_WEIGHT; - public static float MEDIUM_WEIGHT; - public static float HIGH_WEIGHT; - public static float DIAGONAL_WEIGHT; + public static int MOVE_WEIGHT; + public static int MOVE_WEIGHT_DIAG; + public static int OBSTACLE_WEIGHT; + 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; /** Attribute System */ public static int S_WEAPON_STONE; @@ -2567,12 +2573,17 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); + GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); - MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); - HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); - DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); + MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); + OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); + HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); + MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); + MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); // Load AllowedPlayerRaces config file (if exists) final PropertiesParser AllowedPlayerRaces = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE); diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/commons/util/CommonUtil.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/commons/util/CommonUtil.java index 14ae3a4560..ab7837d827 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/commons/util/CommonUtil.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/commons/util/CommonUtil.java @@ -584,4 +584,15 @@ public class CommonUtil final DecimalFormat formatter = new DecimalFormat(format, new DecimalFormatSymbols(Locale.ENGLISH)); return formatter.format(value); } + + /** + * @param numToTest : The number to test. + * @param min : The minimum limit. + * @param max : The maximum limit. + * @return the number or one of the limit (mininum / maximum). + */ + public static int limit(int numToTest, int min, int max) + { + return (numToTest > max) ? max : ((numToTest < min) ? min : numToTest); + } } diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/commons/util/Point2D.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/commons/util/Point2D.java new file mode 100644 index 0000000000..ebb6272e5e --- /dev/null +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/commons/util/Point2D.java @@ -0,0 +1,180 @@ +/* + * 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 . + */ +package org.l2jmobius.commons.util; + +import java.util.Objects; + +/** + * A datatype used to retain a 2D (x/y) point. It got the capability to be set and cleaned. + */ +public class Point2D +{ + protected volatile int _x; + protected volatile int _y; + + public Point2D(int x, int y) + { + _x = x; + _y = y; + } + + @Override + public Point2D clone() + { + return new Point2D(_x, _y); + } + + @Override + public String toString() + { + return _x + ", " + _y; + } + + @Override + public int hashCode() + { + return Objects.hash(_x, _y); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final Point2D other = (Point2D) obj; + return (_x == other._x) && (_y == other._y); + } + + /** + * @param x : The X coord to test. + * @param y : The Y coord to test. + * @return True if all coordinates equals this {@link Point2D} coordinates. + */ + public boolean equals(int x, int y) + { + return (_x == x) && (_y == y); + } + + public int getX() + { + return _x; + } + + public void setX(int x) + { + _x = x; + } + + public int getY() + { + return _y; + } + + public void setY(int y) + { + _y = y; + } + + public void set(int x, int y) + { + _x = x; + _y = y; + } + + /** + * Refresh the current {@link Point2D} using a reference {@link Point2D} and a distance. The new destination is calculated to go in opposite side of the {@link Point2D} reference.
+ *
+ * This method is perfect to calculate fleeing characters position. + * @param referenceLoc : The Point2D used as position. + * @param distance : The distance to be set between current and new position. + */ + public void setFleeing(Point2D referenceLoc, int distance) + { + final double xDiff = referenceLoc.getX() - _x; + final double yDiff = referenceLoc.getY() - _y; + + final double yxRation = Math.abs(xDiff / yDiff); + + final int y = (int) (distance / (yxRation + 1)); + final int x = (int) (y * yxRation); + + _x += (xDiff < 0 ? x : -x); + _y += (yDiff < 0 ? y : -y); + } + + public void clean() + { + _x = 0; + _y = 0; + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @return The distance between this {@Point2D} and some given coordinates. + */ + public double distance2D(int x, int y) + { + final double dx = (double) _x - x; + final double dy = (double) _y - y; + + return Math.sqrt((dx * dx) + (dy * dy)); + } + + /** + * @param point : The {@link Point2D} to test. + * @return The distance between this {@Point2D} and the {@link Point2D} set as parameter. + */ + public double distance2D(Point2D point) + { + return distance2D(point.getX(), point.getY()); + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of some given coordinates. + */ + public boolean isIn2DRadius(int x, int y, int radius) + { + return distance2D(x, y) < radius; + } + + /** + * @param point : The Point2D to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of the {@link Point2D} set as parameter. + */ + public boolean isIn2DRadius(Point2D point, int radius) + { + return distance2D(point) < radius; + } +} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/AttackableAI.java index 547963e3e8..dc29d4cf6e 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/AttackableAI.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/ai/AttackableAI.java @@ -578,7 +578,7 @@ public class AttackableAI extends CreatureAI } // Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast) - final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); + final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); moveTo(moveLoc.getX(), moveLoc.getY(), moveLoc.getZ()); } } @@ -801,7 +801,7 @@ public class AttackableAI extends CreatureAI final int newZ = npc.getZ() + 30; // Mobius: Verify destination. Prevents wall collision issues and fixes monsters not avoiding obstacles. - moveTo(GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); + moveTo(GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); } return; } diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/data/SpawnTable.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/data/SpawnTable.java index a81201f69e..19f0db5b3e 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/data/SpawnTable.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/data/SpawnTable.java @@ -114,8 +114,8 @@ public class SpawnTable } // XML file for spawn - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); // Write info to XML @@ -192,8 +192,8 @@ public class SpawnTable if (update) { - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = spawn.getNpcSpawnTemplate() != null ? spawn.getNpcSpawnTemplate().getSpawnTemplate().getFile() : new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); final File tempFile = new File(spawnFile.getAbsolutePath().substring(Config.DATAPACK_ROOT.getAbsolutePath().length() + 1).replace('\\', '/') + ".tmp"); try diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/enums/GeoType.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/enums/GeoType.java new file mode 100644 index 0000000000..f77aa5eac4 --- /dev/null +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/enums/GeoType.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.enums; + +public enum GeoType +{ + L2J("%d_%d.l2j"), + L2OFF("%d_%d_conv.dat"); + + private final String _filename; + + private GeoType(String filename) + { + _filename = filename; + } + + public String getFilename() + { + return _filename; + } +} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java new file mode 100644 index 0000000000..e62f50a8df --- /dev/null +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java @@ -0,0 +1,144 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 9d255a5f54..7bef9c770b 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,72 +16,63 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.channels.FileChannel.MapMode; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.logging.Level; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; import java.util.logging.Logger; import org.l2jmobius.Config; +import org.l2jmobius.commons.util.CommonUtil; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; -import org.l2jmobius.gameserver.geoengine.geodata.IRegion; -import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; -import org.l2jmobius.gameserver.geoengine.geodata.Region; +import org.l2jmobius.gameserver.enums.GeoType; +import org.l2jmobius.gameserver.enums.MoveDirectionType; +import org.l2jmobius.gameserver.geoengine.geodata.ABlock; +import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; +import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; +import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; +import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.model.interfaces.ILocational; -import org.l2jmobius.gameserver.util.GeoUtils; -import org.l2jmobius.gameserver.util.LinePointIterator; -import org.l2jmobius.gameserver.util.LinePointIterator3D; -/** - * @author -Nemesiss-, HorridoJoho - */ public class GeoEngine { - private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - - private static final int WORLD_MIN_X = -655360; - private static final int WORLD_MIN_Y = -589824; - private static final int WORLD_MIN_Z = -16384; - - /** 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; - - /** The regions array */ - private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; - private static final int ELEVATED_SEE_OVER_DISTANCE = 2; - private static final int MAX_SEE_OVER_HEIGHT = 48; - protected GeoEngine() + private final ABlock[][] _blocks; + private final BlockNull _nullBlock; + + // Pre-allocated buffers. + private final BufferHolder[] _buffers; + + public GeoEngine() { LOGGER.info("GeoEngine: Initializing..."); - for (int i = 0; i < _regions.length(); i++) - { - _regions.set(i, NullRegion.INSTANCE); - } + // Initialize block container. + _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; + + // Load null block. + _nullBlock = new BlockNull(); + + // Initialize multilayer temporarily buffer. + BlockMultilayer.initialize(); + + // Load geo files according to geoengine config setup. int loaded = 0; try { @@ -92,23 +83,52 @@ public class GeoEngine final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); if (Files.exists(geoFilePath)) { - try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + // Region file is load-able, try to load it. + if (loadGeoBlocks(regionX, regionY)) { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); loaded++; } } + else + { + // Region file is not load-able, load null blocks. + loadNullBlocks(regionX, regionY); + } } } } catch (Exception e) { - LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + LOGGER.warning("GeoEngine: Failed to load geodata! " + e); System.exit(1); } - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + // Release multilayer block temporarily buffer. + BlockMultilayer.release(); + + String[] array = Config.PATHFIND_BUFFERS.split(";"); + _buffers = new BufferHolder[array.length]; + + int count = 0; + for (int i = 0; i < array.length; i++) + { + String buf = array[i]; + String[] args = buf.split("x"); + + try + { + int size = Integer.parseInt(args[1]); + count += size; + _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); + } + catch (Exception e) + { + LOGGER.warning("Could not load buffer setting:" + buf + ". " + e); + } + } + LOGGER.info("Loaded " + count + " node buffers."); + // Avoid wrong configs when no files are loaded. if ((loaded == 0) && Config.PATHFINDING) { @@ -118,616 +138,913 @@ public class GeoEngine } /** - * @param geoX - * @param geoY - * @return the region + * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer and log this situation. + * @param size : pre-calculated minimal required size + * @return NodeBuffer : buffer */ - private IRegion getRegion(int geoX, int geoY) + private NodeBuffer getBuffer(int size) { - final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); - if ((region < 0) || (region >= _regions.length())) + NodeBuffer current = null; + for (BufferHolder holder : _buffers) { - return null; - } - return _regions.get(region); - } - - /** - * @param filePath - * @param regionX - * @param regionY - * @throws IOException - */ - 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()).load().order(ByteOrder.LITTLE_ENDIAN))); - } - } - - /** - * @param regionX - * @param regionY - */ - public void unloadRegion(int regionX, int regionY) - { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); - } - - /** - * @param geoX - * @param geoY - * @return if geodata exist - */ - public boolean hasGeoPos(int geoX, int geoY) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return false; - } - return region.hasGeo(); - } - - /** - * Checks the specified position for available geodata. - * @param x the world x - * @param y the world y - * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise - */ - public boolean hasGeo(int x, int y) - { - return hasGeoPos(getGeoX(x), getGeoY(y)); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe check - */ - public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return true; - } - return region.checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe anti-corner cut - */ - public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) - { - boolean can = true; - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - return can && checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the nearest Z value - */ - public int getNearestZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNearestZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next lower Z value - */ - public int getNextLowerZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextLowerZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next higher Z value - */ - public int getNextHigherZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextHigherZ(geoX, geoY, worldZ); - } - - /** - * Gets the Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHeight(int x, int y, int z) - { - return getNearestZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next lower Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getLowerHeight(int x, int y, int z) - { - return getNextLowerZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next higher Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHigherHeight(int x, int y, int z) - { - return getNextHigherZ(getGeoX(x), getGeoY(y), z); - } - - /** - * @param worldX - * @return the geo X - */ - public int getGeoX(int worldX) - { - return (worldX - WORLD_MIN_X) / 16; - } - - /** - * @param worldY - * @return the geo Y - */ - public int getGeoY(int worldY) - { - return (worldY - WORLD_MIN_Y) / 16; - } - - /** - * @param worldZ - * @return the geo Z - */ - public int getGeoZ(int worldZ) - { - return (worldZ - WORLD_MIN_Z) / 16; - } - - /** - * @param geoX - * @return the world X - */ - public int getWorldX(int geoX) - { - return (geoX * 16) + WORLD_MIN_X + 8; - } - - /** - * @param geoY - * @return the world Y - */ - public int getWorldY(int geoY) - { - return (geoY * 16) + WORLD_MIN_Y + 8; - } - - /** - * @param geoZ - * @return the world Z - */ - public int getWorldZ(int geoZ) - { - return (geoZ * 16) + WORLD_MIN_Z + 8; - } - - /** - * Can see target. Doors as target always return true. Checks doors between. - * @param cha the character - * @param target the target - * @return {@code true} if the character can see the target (LOS), {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, WorldObject target) - { - if (target.isDoor()) - { - // Can always see doors. - return true; - } - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); - } - - /** - * Can see target. Checks doors between. - * @param cha the character - * @param worldPosition the world position - * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) - { - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param world - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @param tworld the target's instanceId - * @return - */ - public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) - { - return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instance - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) - { - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) - { - return false; - } - return canSeeTarget(x, y, z, tx, ty, tz); - } - - /** - * @param prevX - * @param prevY - * @param prevGeoZ - * @param curX - * @param curY - * @param nswe - * @return the LOS Z value - */ - private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) - { - if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) - { - throw new RuntimeException("Multiple directions!"); - } - - if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) - { - return getNearestZ(curX, curY, prevGeoZ); - } - - return getNextHigherZ(curX, curY, prevGeoZ); - } - - /** - * Can see target. Does not check doors between. - * @param xValue the x coordinate - * @param yValue the y coordinate - * @param zValue the z coordinate - * @param txValue the target's x coordinate - * @param tyValue the target's y coordinate - * @param tzValue the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) - { - int x = xValue; - int y = yValue; - int tx = txValue; - int ty = tyValue; - - int geoX = getGeoX(x); - int geoY = getGeoY(y); - int tGeoX = getGeoX(tx); - int tGeoY = getGeoY(ty); - - int z = getNearestZ(geoX, geoY, zValue); - int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - // Fastpath. - if ((geoX == tGeoX) && (geoY == tGeoY)) - { - if (hasGeoPos(tGeoX, tGeoY)) - { - return z == tz; - } - return true; - } - - if (tz > z) - { - int tmp = tx; - tx = x; - x = tmp; - - tmp = ty; - ty = y; - y = tmp; - - tmp = tz; - tz = z; - z = tmp; - - tmp = tGeoX; - tGeoX = geoX; - geoX = tmp; - - tmp = tGeoY; - tGeoY = geoY; - geoY = tmp; - } - - final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); - // First point is guaranteed to be available, skip it, we can always see our own position. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - final int prevZ = pointIter.z(); - int prevGeoZ = prevZ; - int ptIndex = 0; - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - - if ((curX == prevX) && (curY == prevY)) + // Find proper size of buffer. + if (holder._size < size) { continue; } - final int beeCurZ = pointIter.z(); - int curGeoZ = prevGeoZ; - - // Check if the position has geodata. - if (hasGeoPos(curX, curY)) + // Find unlocked NodeBuffer. + for (NodeBuffer buffer : holder._buffer) { - final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); - curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); - int maxHeight; - if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) + if (!buffer.isLocked()) { - maxHeight = z + MAX_SEE_OVER_HEIGHT; - } - else - { - maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; + continue; } - boolean canSeeThrough = false; - if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) + return buffer; + } + + LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); + + // NodeBuffer not found, allocate temporary buffer. + current = new NodeBuffer(holder._size); + current.isLocked(); + } + + return current; + } + + /** + * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + * @return boolean : True, when geodata file was loaded without problem. + */ + private boolean loadGeoBlocks(int regionX, int regionY) + { + final String filename = String.format(Config.GEODATA_TYPE.getFilename(), regionX, regionY); + final String filepath = Config.GEODATA_PATH + "\\" + filename; + + // Standard load. + try (RandomAccessFile raf = new RandomAccessFile(filepath, "r"); + FileChannel fc = raf.getChannel()) + { + // Initialize file buffer. + MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // Load 18B header for L2OFF geodata (1st and 2nd byte...region X and Y). + if (Config.GEODATA_TYPE == GeoType.L2OFF) + { + for (int i = 0; i < 18; i++) { - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) + buffer.get(); + } + } + + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Loop over region blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + if (Config.GEODATA_TYPE == GeoType.L2J) { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) - { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); + // Get block type. + final byte type = buffer.get(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + case GeoStructure.TYPE_MULTILAYER_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + default: + { + throw new IllegalArgumentException("Unknown block type: " + type); + } + } } else { - canSeeThrough = true; + // Get block type. + final short type = buffer.getShort(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + default: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + } } } + } + + // Check data consistency. + if (buffer.remaining() > 0) + { + LOGGER.warning("Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); + } + + // Loading was successful. + return true; + } + catch (Exception e) + { + // An error occured while loading, load null blocks. + LOGGER.warning("Error loading " + filename + " region file. " + e); + + // Replace whole region file with null blocks. + loadNullBlocks(regionX, regionY); + + // Loading was not successful. + return false; + } + } + + /** + * Loads null blocks. Used when no region file is detected or an error occurs during loading. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + */ + private void loadNullBlocks(int regionX, int regionY) + { + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Load all null blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + _blocks[blockX + ix][blockY + iy] = _nullBlock; + } + } + } + + /** + * Converts world X to geodata X. + * @param worldX + * @return int : Geo X + */ + public static int getGeoX(int worldX) + { + return (worldX - World.WORLD_X_MIN) >> 4; + } + + /** + * Converts world Y to geodata Y. + * @param worldY + * @return int : Geo Y + */ + public static int getGeoY(int worldY) + { + return (worldY - World.WORLD_Y_MIN) >> 4; + } + + /** + * Converts geodata X to world X. + * @param geoX + * @return int : World X + */ + public static int getWorldX(int geoX) + { + return (geoX << 4) + World.WORLD_X_MIN + 8; + } + + /** + * Converts geodata Y to world Y. + * @param geoY + * @return int : World Y + */ + public static int getWorldY(int geoY) + { + return (geoY << 4) + World.WORLD_Y_MIN + 8; + } + + /** + * Returns block of geodata on given coordinates. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return {@link ABlock} : Block of geodata. + */ + public ABlock getBlock(int geoX, int geoY) + { + final int x = geoX / GeoStructure.BLOCK_CELLS_X; + if ((x < 0) || (x >= GeoStructure.GEO_BLOCKS_X)) + { + return null; + } + final int y = geoY / GeoStructure.BLOCK_CELLS_Y; + if ((y < 0) || (y >= GeoStructure.GEO_BLOCKS_Y)) + { + return null; + } + return _blocks[x][y]; + } + + /** + * Check if geo coordinates has geo. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return boolean : True, if given geo coordinates have geodata + */ + public boolean hasGeoPos(int geoX, int geoY) + { + final ABlock block = getBlock(geoX, geoY); + return (block != null) && block.hasGeoPos(); + } + + /** + * Returns the height of cell, which is closest to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell geodata Z coordinate, closest to given coordinates. + */ + public short getHeightNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return (short) worldZ; + } + return block.getHeightNearest(geoX, geoY, worldZ); + } + + /** + * Returns the NSWE flag byte of cell, which is closes to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. + */ + public byte getNsweNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return GeoStructure.CELL_FLAG_ALL; + } + return block.getNsweNearest(geoX, geoY, worldZ); + } + + /** + * Check if world coordinates has geo. + * @param worldX : World X + * @param worldY : World Y + * @return boolean : True, if given world coordinates have geodata + */ + public boolean hasGeo(int worldX, int worldY) + { + return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param loc : The location used as reference. + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(Location loc) + { + return getHeightNearest(getGeoX(loc.getX()), getGeoY(loc.getY()), loc.getZ()); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param worldX : world x + * @param worldY : world y + * @param worldZ : world z + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(int worldX, int worldY, int worldZ) + { + return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); + } + + /** + * Check line of sight from {@link WorldObject} to {@link WorldObject}.
+ * @param object : The origin object. + * @param target : The target object. + * @return True, when object can see target. + */ + public boolean canSeeTarget(WorldObject object, WorldObject target) + { + // Can always see doors. + if (target.isDoor()) + { + return true; + } + + if (object.getInstanceWorld() != target.getInstanceWorld()) + { + return false; + } + + if (DoorData.getInstance().checkIfDoorsBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld())) + { + return false; + } + + // Get object's and target's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + double theight = 0; + if (target instanceof Creature) + { + theight += (((Creature) target).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + return canSee(object.getX(), object.getY(), object.getZ(), oheight, target.getX(), target.getY(), target.getZ(), theight) && canSee(target.getX(), target.getY(), target.getZ(), theight, object.getX(), object.getY(), object.getZ(), oheight); + } + + /** + * Check line of sight from {@link WorldObject} to {@link Location}.
+ * Note: The check uses {@link Location}'s real Z coordinate (e.g. point above ground), not its geodata representation. + * @param object : The origin object. + * @param position : The target position. + * @return True, when object can see position. + */ + public boolean canSeeLocation(WorldObject object, Location position) + { + // Get object and location coordinates. + int ox = object.getX(); + int oy = object.getY(); + int oz = object.getZ(); + int tx = position.getX(); + int ty = position.getY(); + int tz = position.getZ(); + + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld())) + { + return false; + } + + // Get object's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + // Perform geodata check. + return canSee(ox, oy, oz, oheight, tx, ty, tz, 0) && canSee(tx, ty, tz, 0, ox, oy, oz, oheight); + } + + /** + * Simple check for origin to target visibility.
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param oheight : The height of origin, used as start point. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param theight : The height of target, used as end point. + * @return True, when origin can see target. + */ + public boolean canSee(int ox, int oy, int oz, double oheight, int tx, int ty, int tz, double theight) + { + // Get origin geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get target geodata coordinates. + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check being on same cell and layer (index). + // Note: Get index must use origin height increased by cell height, the method returns index to height exclusive self. + int index = block.getIndexBelow(gox, goy, oz + GeoStructure.CELL_HEIGHT); + if (index < 0) + { + return false; + } + + if ((gox == gtx) && (goy == gty)) + { + return index == block.getIndexBelow(gtx, gty, tz + GeoStructure.CELL_HEIGHT); + } + + // Get ground and nswe flag. + int groundZ = block.getHeight(index); + int nswe = block.getNswe(index); + + // Get delta coordinates, slope of line (XY, XZ) and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double dz = (tz + theight) - (oz + oheight); + final double m = (double) dy / dx; + final double mz = dz / Math.sqrt((dx * dx) + (dy * dy)); + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); + + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) + { + // Set next cell in X direction. + gridX += mdt.getStepX(); + gox += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); - if (!canSeeThrough) - { - return false; - } + // Set next cell in Y direction. + gridY += mdt.getStepY(); + goy += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevGeoZ = curGeoZ; - ++ptIndex; + // Get block of the next cell. + block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get line of sight height (including Z slope). + double losz = oz + oheight + Config.MAX_OBSTACLE_HEIGHT; + losz += mz * Math.sqrt(((checkX - ox) * (checkX - ox)) + ((checkY - oy) * (checkY - oy))); + + // Check line of sight going though wall (vertical check). + + // Get index of particular layer, based on last iterated cell conditions. + boolean canMove = (nswe & dir) != 0; + if (canMove) + { + // No wall present, get next cell below current cell. + index = block.getIndexBelow(gox, goy, groundZ + GeoStructure.CELL_IGNORE_HEIGHT); + } + else + { + // Wall present, get next cell above current cell. + index = block.getIndexAbove(gox, goy, groundZ - (2 * GeoStructure.CELL_HEIGHT)); + } + + // Next cell's does not exist (no geodata with valid condition), return fail. + if (index < 0) + { + return false; + } + + // Get next cell's layer height. + int z = block.getHeight(index); + + // Perform sine of sight check (next cell is above line of sight line), return fail. + if (z > losz) + { + return false; + } + + // Next cell is accessible, update z and NSWE. + groundZ = z; + nswe = block.getNswe(index); } + // Iteration is completed, no obstacle is found. return true; } /** - * Move check. - * @param x the x coordinate - * @param y the y coordinate - * @param zValue the z coordinate - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tzValue the target's z coordinate - * @param instance the instance - * @return the last Location (x,y,z) where player can walk - just before wall + * Check movement from coordinates to coordinates.
+ * Note: The Z coordinates are supposed to be already validated geodata coordinates. + * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return True, when target coordinates are reachable from origin coordinates. */ - public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(x); - final int geoY = getGeoY(y); - final int z = getNearestZ(geoX, geoY, zValue); - final int tGeoX = getGeoX(tx); - final int tGeoY = getGeoY(ty); - final int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return new Location(x, y, getHeight(x, y, z)); - } - if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) - { - return new Location(x, y, getHeight(x, y, z)); + return false; } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = z; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return false; + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + int goz = getHeightNearest(gox, goy, oz); + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check movement within same cell. + if ((gox == gtx) && (goy == gty)) + { + return goz == getHeight(tx, ty, tz); + } + + // Get nswe flag. + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - // Can't move, return previous location. - return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check point heading into obstacle, if so return current point. + if ((nswe & dir) == 0) + { + return false; + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return false; + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != tz)) - { - // Different floors, return start location. - return new Location(x, y, z); - } - - return new Location(tx, ty, tz); + // When origin Z is target Z, the move is successful. + return goz == getHeight(tx, ty, tz); } /** - * Checks if its possible to move from one location to another. - * @param fromX the X coordinate to start checking from - * @param fromY the Y coordinate to start checking from - * @param fromZvalue the Z coordinate to start checking from - * @param toX the X coordinate to end checking at - * @param toY the Y coordinate to end checking at - * @param toZvalue the Z coordinate to end checking at - * @param instance the instance - * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + * Check movement from origin to target coordinates. Returns last available point in the checked path.
+ * Target X and Y reachable and Z is on same floor: + *
    + *
  • Location of the target with corrected Z value from geodata.
  • + *
+ * Target X and Y reachable but Z is on another floor: + *
    + *
  • Location of the origin with corrected Z value from geodata.
  • + *
+ * Target X and Y not reachable: + *
    + *
  • Last accessible location in destination to target.
  • + *
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return The {@link Location} representing last point of movement (e.g. just before wall). */ - public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + public Location getValidLocation(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(fromX); - final int geoY = getGeoY(fromY); - final int fromZ = getNearestZ(geoX, geoY, fromZvalue); - final int tGeoX = getGeoX(toX); - final int tGeoY = getGeoY(toY); - final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); - - if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) - { - return false; + return new Location(ox, oy, oz); } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = fromZ; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return new Location(ox, oy, oz); + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + final int gtz = getHeightNearest(gtx, gty, tz); + int goz = getHeightNearest(gox, goy, oz); + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - return false; - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check target cell is out of geodata grid (world coordinates). + if ((nx < 0) || (nx >= GeoStructure.GEO_CELLS_X) || (ny < 0) || (ny >= GeoStructure.GEO_CELLS_Y)) + { + return new Location(checkX, checkY, goz); + } + + // Check point heading into obstacle, if so return current (border) point. + if ((nswe & dir) == 0) + { + return new Location(checkX, checkY, goz); + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current (border) point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return new Location(checkX, checkY, goz); + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) - { - // Different floors. - return false; - } - - return true; + // Compare Z coordinates: + // If same, path is okay, return target point and fix its Z geodata coordinate. + // If not same, path is does not exist, return origin point. + return goz == gtz ? new Location(tx, ty, gtz) : new Location(ox, oy, oz); } + /** + * Returns the list of location objects as a result of complete path calculation. + * @param ox : origin x + * @param oy : origin y + * @param oz : origin z + * @param tx : target x + * @param ty : target y + * @param tz : target z + * @param instance + * @return {@code List} : complete path from nodes + */ + public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + { + // Get origin and check existing geo coords. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + if (!hasGeoPos(gox, goy)) + { + return Collections.emptyList(); + } + + int goz = getHeightNearest(gox, goy, oz); + + // Get target and check existing geo coords. + int gtx = getGeoX(tx); + int gty = getGeoY(ty); + if (!hasGeoPos(gtx, gty)) + { + return Collections.emptyList(); + } + + int gtz = getHeightNearest(gtx, gty, tz); + + // Prepare buffer for pathfinding calculations. + NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + if (buffer == null) + { + return Collections.emptyList(); + } + + // Find path. + List path = null; + try + { + path = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + if (path.isEmpty()) + { + return Collections.emptyList(); + } + } + catch (Exception e) + { + return Collections.emptyList(); + } + finally + { + buffer.free(); + } + + // Check path. + if (path.size() < 3) + { + return path; + } + + // Get path list iterator. + ListIterator point = path.listIterator(); + + // Get node A (origin). + int nodeAx = ox; + int nodeAy = oy; + int nodeAz = goz; + + // Get node B. + Location nodeB = point.next(); + + // Iterate thought the path to optimize it. + while (point.hasNext()) + { + // Get node C. + Location nodeC = path.get(point.nextIndex()); + + // Check movement from node A to node C. + if (canMoveToTarget(nodeAx, nodeAy, nodeAz, nodeC.getX(), nodeC.getY(), nodeC.getZ(), instance)) + { + // Can move from node A to node C. + // Remove node B. + point.remove(); + } + else + { + // Can not move from node A to node C. + // Set node A (node B is part of path, update A coordinates). + nodeAx = nodeB.getX(); + nodeAy = nodeB.getY(); + nodeAz = nodeB.getZ(); + } + + // Set node B. + nodeB = point.next(); + } + + return path; + } + + /** + * NodeBuffer container with specified size and count of separate buffers. + */ + private static class BufferHolder + { + final int _size; + final List _buffer; + + public BufferHolder(int size, int count) + { + _size = size; + _buffer = new ArrayList<>(count); + + for (int i = 0; i < count; i++) + { + _buffer.add(new NodeBuffer(size)); + } + } + } + + /** + * Returns the instance of the {@link GeoEngine}. + * @return {@link GeoEngine} : The instance. + */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -737,4 +1054,4 @@ public class GeoEngine { protected static final GeoEngine INSTANCE = new GeoEngine(); } -} +} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java deleted file mode 100644 index cc76b7523a..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ /dev/null @@ -1,301 +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 . - */ -package org.l2jmobius.gameserver.geoengine; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; -import org.l2jmobius.gameserver.model.World; -import org.l2jmobius.gameserver.model.instancezone.Instance; - -/** - * @author -Nemesiss- - */ -public class GeoEnginePathfinding -{ - private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); - - private BufferInfo[] _buffers; - - protected GeoEnginePathfinding() - { - try - { - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - - _buffers = 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); - } - - _buffers[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"); - } - } - - public boolean pathNodesExist(short regionoffset) - { - return false; - } - - public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) - { - 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)))); - if (buffer == null) - { - return null; - } - - List path = null; - try - { - final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); - - if (result == null) - { - return null; - } - - path = constructPath(result); - } - catch (Exception e) - { - LOGGER.warning(e.getMessage()); - return null; - } - finally - { - buffer.free(); - } - - // check path - if (path.size() < 3) - { - return path; - } - - int currentX, currentY, currentZ; - ListIterator middlePoint; - - middlePoint = path.listIterator(); - currentX = x; - currentY = y; - currentZ = z; - - while (middlePoint.hasNext()) - { - final AbstractNodeLoc locMiddle = middlePoint.next(); - if (!middlePoint.hasNext()) - { - break; - } - - final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); - if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) - { - middlePoint.remove(); - } - else - { - currentX = locMiddle.getX(); - currentY = locMiddle.getY(); - currentZ = locMiddle.getZ(); - } - } - - return path; - } - - /** - * Convert geodata position to pathnode position - * @param geo_pos - * @return pathnode position - */ - public short getNodePos(int geo_pos) - { - return (short) (geo_pos >> 3); // OK? - } - - /** - * Convert node position to pathnode block position - * @param node_pos - * @return pathnode block position (0...255) - */ - public short getNodeBlock(int node_pos) - { - return (short) (node_pos % 256); - } - - public byte getRegionX(int node_pos) - { - return (byte) ((node_pos >> 8) + World.TILE_X_MIN); - } - - public byte getRegionY(int node_pos) - { - return (byte) ((node_pos >> 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 node_x rx - * @return - */ - public int calculateWorldX(short node_x) - { - return World.MAP_MIN_X + (node_x * 128) + 48; - } - - /** - * Convert pathnode y to World y position - * @param node_y - * @return - */ - public int calculateWorldY(short node_y) - { - return World.MAP_MIN_Y + (node_y * 128) + 48; - } - - private List constructPath(AbstractNode nodeValue) - { - final LinkedList path = new LinkedList<>(); - int previousDirectionX = Integer.MIN_VALUE; - int previousDirectionY = Integer.MIN_VALUE; - int directionX, directionY; - - AbstractNode node = nodeValue; - while (node.getParent() != null) - { - directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); - directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); - - // only add a new route point if moving direction changes - if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) - { - previousDirectionX = directionX; - previousDirectionY = directionY; - - path.addFirst(node.getLoc()); - node.setLoc(null); - } - - node = node.getParent(); - } - - return path; - } - - private CellNodeBuffer alloc(int size) - { - CellNodeBuffer current = null; - for (BufferInfo i : _buffers) - { - if (i.mapSize >= size) - { - for (CellNodeBuffer buf : i.buffer) - { - if (buf.lock()) - { - current = buf; - break; - } - } - if (current != null) - { - break; - } - - // not found, allocate temporary buffer - current = new CellNodeBuffer(i.mapSize); - current.lock(); - if (i.buffer.size() < i.count) - { - i.buffer.add(current); - break; - } - } - } - - return current; - } - - private static final class BufferInfo - { - final int mapSize; - final int count; - ArrayList buffer; - - public BufferInfo(int size, int cnt) - { - mapSize = size; - count = cnt; - buffer = new ArrayList<>(count); - } - } - - public static GeoEnginePathfinding getInstance() - { - return SingletonHolder.INSTANCE; - } - - private static class SingletonHolder - { - protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); - } -} diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java new file mode 100644 index 0000000000..49ec35aa1c --- /dev/null +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java @@ -0,0 +1,85 @@ +/* + * 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 . + */ +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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @param index : Index of the cell. + * @return short : Cell geodata Z coordinate, below given coordinates. + */ + public abstract byte getNswe(int index); +} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java new file mode 100644 index 0000000000..f5997bf287 --- /dev/null +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java @@ -0,0 +1,130 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java new file mode 100644 index 0000000000..9af9d2a28a --- /dev/null +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java @@ -0,0 +1,95 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java new file mode 100644 index 0000000000..31fbf5d477 --- /dev/null +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java new file mode 100644 index 0000000000..f77d21d84b --- /dev/null +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java @@ -0,0 +1,68 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +public class BlockNull extends ABlock +{ + @Override + public boolean hasGeoPos() + { + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java deleted file mode 100644 index 61ea4fcf82..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java +++ /dev/null @@ -1,48 +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 . - */ -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() - { - } -} diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java deleted file mode 100644 index 3c8de1a7f7..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java +++ /dev/null @@ -1,77 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java deleted file mode 100644 index 1ccdefe3da..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java +++ /dev/null @@ -1,56 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java new file mode 100644 index 0000000000..691b164e0c --- /dev/null +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ +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_ALL = 0x0F; + + // 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; +} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java deleted file mode 100644 index 25eafc5a04..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java +++ /dev/null @@ -1,42 +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 . - */ -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); -} diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java deleted file mode 100644 index 0d2c765002..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java +++ /dev/null @@ -1,47 +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 . - */ -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(); -} diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java deleted file mode 100644 index 52df457360..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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); - - // 1 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; - } -} diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java deleted file mode 100644 index 72a5a635c7..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java +++ /dev/null @@ -1,55 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author HorridoJoho - */ -public final class NullRegion implements IRegion -{ - public static final NullRegion INSTANCE = new NullRegion(); - - @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 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; - } -} diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java deleted file mode 100644 index fc924cb875..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java +++ /dev/null @@ -1,92 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java deleted file mode 100644 index 5087513ed9..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java +++ /dev/null @@ -1,87 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public abstract class AbstractNode -{ - private T _loc; - private AbstractNode _parent; - - public AbstractNode(T loc) - { - _loc = loc; - } - - public void setParent(AbstractNode p) - { - _parent = p; - } - - public AbstractNode getParent() - { - return _parent; - } - - public T getLoc() - { - return _loc; - } - - public void setLoc(T l) - { - _loc = l; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); - return result; - } - - @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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java deleted file mode 100644 index 7223b5b2be..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java +++ /dev/null @@ -1,33 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -/** - * @author -Nemesiss- - */ -public abstract class AbstractNodeLoc -{ - public abstract int getX(); - - public abstract int getY(); - - public abstract int getZ(); - - public abstract int getNodeX(); - - public abstract int getNodeY(); -} diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java deleted file mode 100644 index 3425234e0e..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public class CellNode extends AbstractNode -{ - 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java deleted file mode 100644 index 522610bb7c..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java +++ /dev/null @@ -1,348 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -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 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) - { - _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(); - } - - public final List debugPath() - { - final List 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 void getNeighbors() - { - if (!_current.getLoc().canGoAll()) - { - 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); - } - - // SouthEast - if ((nodeE != null) && (nodeS != null)) - { - if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) - { - addNode(x + 1, y + 1, z, true); - } - } - - // SouthWest - if ((nodeS != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) - { - addNode(x - 1, y + 1, z, true); - } - } - - // NorthEast - if ((nodeN != null) && (nodeE != null)) - { - if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) - { - addNode(x + 1, y - 1, z, true); - } - } - - // NorthWest - if ((nodeN != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) - { - addNode(x - 1, y - 1, z, true); - } - } - } - - private 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(); - // reinit node if needed - if (result.getLoc() != null) - { - result.getLoc().set(x, y, z); - } - else - { - result.setLoc(new NodeLoc(x, y, z)); - } - } - - return result; - } - - private 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 boolean isHighWeight(int x, int y, int z) - { - final CellNode result = getNode(x, y, z); - if (result == null) - { - return true; - } - - if (!result.getLoc().canGoAll()) - { - return true; - } - if (Math.abs(result.getLoc().getZ() - z) > 16) - { - return true; - } - - return false; - } - - private 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java new file mode 100644 index 0000000000..fa5ca43c2f --- /dev/null +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -0,0 +1,111 @@ +/* + * 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 . + */ +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 geodata values. + private int _geoX; + private int _geoY; + private byte _nswe; + + // 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; + + _costG = 0; + _costH = 0; + _costF = 0; + + _parent = null; + } + + public void setGeo(int gx, int gy, int gz, byte nswe) + { + super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); + + _geoX = gx; + _geoY = gy; + _nswe = nswe; + } + + 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 int getCostF() + { + return _costF; + } + + public Node getParent() + { + return _parent; + } + + @Override + public int compareTo(Node o) + { + return _costF - o._costF; + } +} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java new file mode 100644 index 0000000000..b464212c96 --- /dev/null +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -0,0 +1,368 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.awt.Color; +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; +import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; + +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 _opened; + // Container holding Nodes already explored. + private final List _closed; + + // Target coordinates. + private int _gtx; + private int _gty; + private int _gtz; + + // Pathfinding statistics. + private long _timeStamp; + private long _lastElapsedTime; + + 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 findPath(int gox, int goy, int goz, int gtx, int gty, int gtz) + { + // Set start timestamp. + _timeStamp = System.currentTimeMillis(); + + // 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)); + _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 constructPath() + { + // Create result. + final LinkedList 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; + } + + /** + * Creates list of Nodes to show debug path. + * @param debug : The debug packet to add debug informations in. + */ + public void debugPath(ExServerPrimitive debug) + { + // Add all opened node as yellow points. + for (Node n : _opened) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); + } + + // Add all opened node as blue points. + for (Node n : _closed) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); + } + } + + 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; + + _lastElapsedTime = System.currentTimeMillis() - _timeStamp; + _lock.unlock(); + } + + public long getElapsedTime() + { + return _lastElapsedTime; + } + + /** + * Expand the current {@link Node} by exploring its neighbors (axially and diagonally). + */ + private void expand() + { + // Movement is blocked, skip. + final byte nswe = _current.getNSWE(); + if (nswe == 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; + + // Can move north, expand. + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + } + + // Can move south, expand. + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + } + + // Can move west, expand. + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move east, expand. + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move north-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move north-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + } + + /** + * 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 weight : The weight of movement to the new node. + * @return The nswe of the added node. Blank, if not added. + */ + private byte addNode(int gx, int gy, int gzValue, int weight) + { + // 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]; + + // Set node geodata coordinates. + node.setGeo(gx, gy, gz, nswe); + + // 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, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + _opened.add(node); + _bufferIndex++; + return nswe; + } + + /** + * Calculate cost H value, calculated using diagonal distance method.
+ * 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. + final int dx = Math.abs(gx - _gtx); + final int dy = Math.abs(gy - _gty); + final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + + // Get diagonal and axial differences to the target. + final int dd = Math.min(dx, dy); + final int da = Math.max(dx, dy) - dd; + + // Calculate the diagonal distance of the node to the target. + return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + } +} \ No newline at end of file diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java deleted file mode 100644 index e3bad04b25..0000000000 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; - -/** - * @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 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 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; - // return super.hashCode(); - } - - @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; - if (_x != other._x) - { - return false; - } - if (_y != other._y) - { - return false; - } - if (_goNorth != other._goNorth) - { - return false; - } - if (_goEast != other._goEast) - { - return false; - } - if (_goSouth != other._goSouth) - { - return false; - } - if (_goWest != other._goWest) - { - return false; - } - if (_geoHeight != other._geoHeight) - { - return false; - } - return true; - } -} diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java index fec7e24f5e..c93649d8fd 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java @@ -94,15 +94,15 @@ public class ZoneManager implements IXmlReader private static final Map SETTINGS = new HashMap<>(); private static final int SHIFT_BY = 15; - private static final int OFFSET_X = Math.abs(World.MAP_MIN_X >> SHIFT_BY); - private static final int OFFSET_Y = Math.abs(World.MAP_MIN_Y >> SHIFT_BY); + private static final int OFFSET_X = Math.abs(World.WORLD_X_MIN >> SHIFT_BY); + private static final int OFFSET_Y = Math.abs(World.WORLD_Y_MIN >> SHIFT_BY); private final Map, ConcurrentHashMap> _classZones = new ConcurrentHashMap<>(); private final Map _spawnTerritories = new ConcurrentHashMap<>(); private final AtomicInteger _lastDynamicId = new AtomicInteger(300000); private List _debugItems; - private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.MAP_MAX_X >> SHIFT_BY) + OFFSET_X + 1][(World.MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y + 1]; + private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.WORLD_X_MAX >> SHIFT_BY) + OFFSET_X + 1][(World.WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y + 1]; /** * Instantiates a new zone manager. diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/Location.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/Location.java index 73689ab1a6..ca3b22a3f0 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/Location.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/Location.java @@ -16,30 +16,30 @@ */ package org.l2jmobius.gameserver.model; +import java.util.Objects; + +import org.l2jmobius.commons.util.Point2D; import org.l2jmobius.gameserver.model.interfaces.ILocational; import org.l2jmobius.gameserver.model.interfaces.IPositionable; /** - * Location data transfer object.
- * Contains coordinates data, heading and instance Id. - * @author Zoey76 + * A datatype used to retain a 3D (x/y/z/heading) point. It got the capability to be set and cleaned. */ -public class Location implements IPositionable +public class Location extends Point2D implements IPositionable { - protected int _x; - protected int _y; - protected int _z; - private int _heading; + protected volatile int _z; + protected volatile int _heading; public Location(int x, int y, int z) { - this(x, y, z, 0); + super(x, y); + _z = z; + _heading = 0; } public Location(int x, int y, int z, int heading) { - _x = x; - _y = y; + super(x, y); _z = z; _heading = heading; } @@ -51,9 +51,8 @@ public class Location implements IPositionable public Location(StatSet set) { - _x = set.getInt("x"); - _y = set.getInt("y"); - _z = set.getInt("z"); + super(set.getInt("x", 0), set.getInt("y", 0)); + _z = set.getInt("z", 0); _heading = set.getInt("heading", 0); } @@ -146,6 +145,25 @@ public class Location implements IPositionable _heading = loc.getHeading(); } + @Override + public void clean() + { + super.clean(); + _z = 0; + } + + @Override + public Location clone() + { + return new Location(_x, _y, _z); + } + + @Override + public int hashCode() + { + return (31 * super.hashCode()) + Objects.hash(_z); + } + @Override public boolean equals(Object obj) { diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/World.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/World.java index c1510e8c0e..0d796dd834 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/World.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/World.java @@ -66,19 +66,19 @@ public class World public static final int TILE_Y_MAX = 26; public static final int TILE_ZERO_COORD_X = 20; public static final int TILE_ZERO_COORD_Y = 18; - public static final int MAP_MIN_X = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; - public static final int MAP_MIN_Y = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; + public static final int WORLD_X_MIN = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; + public static final int WORLD_Y_MIN = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; - public static final int MAP_MAX_X = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; - public static final int MAP_MAX_Y = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; + public static final int WORLD_X_MAX = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; + public static final int WORLD_Y_MAX = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; /** Calculated offset used so top left region is 0,0 */ - public static final int OFFSET_X = Math.abs(MAP_MIN_X >> SHIFT_BY); - public static final int OFFSET_Y = Math.abs(MAP_MIN_Y >> SHIFT_BY); + public static final int OFFSET_X = Math.abs(WORLD_X_MIN >> SHIFT_BY); + public static final int OFFSET_Y = Math.abs(WORLD_Y_MIN >> SHIFT_BY); /** Number of regions. */ - private static final int REGIONS_X = (MAP_MAX_X >> SHIFT_BY) + OFFSET_X; - private static final int REGIONS_Y = (MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y; + private static final int REGIONS_X = (WORLD_X_MAX >> SHIFT_BY) + OFFSET_X; + private static final int REGIONS_Y = (WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y; /** Map containing all the players in game. */ private static final Map _allPlayers = new ConcurrentHashMap<>(); @@ -817,9 +817,6 @@ public class World return _memberInPartyNumber.get(); } - /** - * @return the current instance of World - */ public static World getInstance() { return SingletonHolder.INSTANCE; diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/WorldObject.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/WorldObject.java index 0e5536bbc0..62313ffea1 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/WorldObject.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/WorldObject.java @@ -188,23 +188,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif synchronized (this) { int spawnX = x; - if (spawnX > World.MAP_MAX_X) + if (spawnX > World.WORLD_X_MAX) { - spawnX = World.MAP_MAX_X - 5000; + spawnX = World.WORLD_X_MAX - 5000; } - if (spawnX < World.MAP_MIN_X) + if (spawnX < World.WORLD_X_MIN) { - spawnX = World.MAP_MIN_X + 5000; + spawnX = World.WORLD_X_MIN + 5000; } int spawnY = y; - if (spawnY > World.MAP_MAX_Y) + if (spawnY > World.WORLD_Y_MAX) { - spawnY = World.MAP_MAX_Y - 5000; + spawnY = World.WORLD_Y_MAX - 5000; } - if (spawnY < World.MAP_MIN_Y) + if (spawnY < World.WORLD_Y_MIN) { - spawnY = World.MAP_MIN_Y + 5000; + spawnY = World.WORLD_Y_MIN + 5000; } // Set the x,y,z position of the WorldObject. If flagged with _isSpawned, setXYZ will automatically update world region, so avoid that. @@ -518,23 +518,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif public void setXYZInvisible(int x, int y, int z) { int correctX = x; - if (correctX > World.MAP_MAX_X) + if (correctX > World.WORLD_X_MAX) { - correctX = World.MAP_MAX_X - 5000; + correctX = World.WORLD_X_MAX - 5000; } - if (correctX < World.MAP_MIN_X) + if (correctX < World.WORLD_X_MIN) { - correctX = World.MAP_MIN_X + 5000; + correctX = World.WORLD_X_MIN + 5000; } int correctY = y; - if (correctY > World.MAP_MAX_Y) + if (correctY > World.WORLD_Y_MAX) { - correctY = World.MAP_MAX_Y - 5000; + correctY = World.WORLD_Y_MAX - 5000; } - if (correctY < World.MAP_MIN_Y) + if (correctY < World.WORLD_Y_MIN) { - correctY = World.MAP_MIN_Y + 5000; + correctY = World.WORLD_Y_MIN + 5000; } setXYZ(correctX, correctY, z); diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/Creature.java index 5ddd5eecff..e30f84b8f7 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -65,8 +65,6 @@ 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.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -2585,7 +2583,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3405,8 +3403,8 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe final int originalX = x; final int originalY = y; final int originalZ = z; - final int gtx = (originalX - World.MAP_MIN_X) >> 4; - final int gty = (originalY - World.MAP_MIN_Y) >> 4; + final int gtx = (originalX - World.WORLD_X_MIN) >> 4; + final int gty = (originalY - World.WORLD_Y_MIN) >> 4; if (isOnGeodataPath()) { try @@ -3429,7 +3427,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe && !(((curZ - z) > 300) && (distance < 300))) // Prohibit correcting destination if character wants to fall. { // location different if destination wasn't reached (or just z coord is different) - final Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(curX, curY, curZ, x, y, z, getInstanceWorld()); + final Location destiny = GeoEngine.getInstance().getValidLocation(curX, curY, curZ, x, y, z, getInstanceWorld()); x = destiny.getX(); y = destiny.getY(); z = destiny.getZ(); @@ -3443,7 +3441,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { if (isPlayer() && !_isFlying && !isInWater) diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/Summon.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/Summon.java index 87d65e23d3..ad18df7166 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/Summon.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/Summon.java @@ -106,7 +106,7 @@ public abstract class Summon extends Playable final int x = owner.getX(); final int y = owner.getY(); final int z = owner.getZ(); - final Location location = GeoEngine.getInstance().canMoveToTargetLoc(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, owner.getInstanceWorld()); + final Location location = GeoEngine.getInstance().getValidLocation(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, getInstanceWorld()); setXYZInvisible(location.getX(), location.getY(), location.getZ()); } diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index 29b05143a1..5675dc61e1 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -14275,8 +14275,8 @@ public class PlayerInstance extends Playable public boolean isInTimedHuntingZone(int zoneId, int locX, int locY) { - final int x = ((locX - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((locY - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((locX - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((locY - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; switch (zoneId) { diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java index abbe632775..8943842d96 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java @@ -1569,7 +1569,7 @@ public class ItemInstance extends WorldObject if (dropper != null) { final Instance instance = dropper.getInstanceWorld(); - final Location dropDest = GeoEngine.getInstance().canMoveToTargetLoc(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); + final Location dropDest = GeoEngine.getInstance().getValidLocation(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); x = dropDest.getX(); y = dropDest.getY(); z = dropDest.getZ(); diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java index b2be9fb5e2..0bc496e8cc 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java @@ -1179,7 +1179,7 @@ public class SkillCaster implements Runnable } } - final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().canMoveToTargetLoc(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); + final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().getValidLocation(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); creature.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); creature.broadcastPacket(new FlyToLocation(creature, destination, flyType, 0, 0, 333)); diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java index c62c821f44..30068134e3 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java @@ -114,7 +114,7 @@ public class ValidatePosition implements IClientIncomingPacket { if (player.isFalling(_z)) { - final int nearestZ = GeoEngine.getInstance().getHigherHeight(_x, _y, _z); + final int nearestZ = GeoEngine.getInstance().getHeight(_x, _y, _z); if (player.getZ() < nearestZ) { player.setXYZ(_x, _y, nearestZ); diff --git a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/util/GeoUtils.java index 4600a0fc53..19a979819e 100644 --- a/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_7.0_PreludeOfWar/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -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.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; @@ -30,21 +30,21 @@ public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getInstance().getWorldX(iter.x()); - final int wy = GeoEngine.getInstance().getWorldY(iter.y()); + final int wx = GeoEngine.getWorldX(iter.x()); + final int wy = GeoEngine.getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public final class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); - int wy = GeoEngine.getInstance().getWorldY(prevY); + int wx = GeoEngine.getWorldX(prevX); + int wy = GeoEngine.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.getInstance().getWorldX(curX); - wy = GeoEngine.getInstance().getWorldY(curY); + wx = GeoEngine.getWorldX(curX); + wy = GeoEngine.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().checkNearestNswe(x, y, z, nswe)) + if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0) { return Color.GREEN; } @@ -109,9 +109,8 @@ public final class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final GeoEngine ge = GeoEngine.getInstance(); - final int playerGx = ge.getGeoX(player.getX()); - final int playerGy = ge.getGeoY(player.getY()); + final int playerGx = GeoEngine.getGeoX(player.getX()); + final int playerGy = GeoEngine.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -135,32 +134,32 @@ public final class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = ge.getWorldX(gx); - final int y = ge.getWorldY(gy); - final int z = ge.getNearestZ(gx, gy, player.getZ()); + final int x = GeoEngine.getWorldX(gx); + final int y = GeoEngine.getWorldY(gy); + final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); + Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); 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, Cell.NSWE_EAST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); 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, Cell.NSWE_SOUTH); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); 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, Cell.NSWE_WEST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); 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); @@ -188,41 +187,41 @@ public final class GeoUtils { if (y > lastY) { - return Cell.NSWE_SOUTH_EAST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E; } else if (y < lastY) { - return Cell.NSWE_NORTH_EAST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E; } else { - return Cell.NSWE_EAST; + return GeoStructure.CELL_FLAG_E; } } else if (x < lastX) // west { if (y > lastY) { - return Cell.NSWE_SOUTH_WEST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W; } else if (y < lastY) { - return Cell.NSWE_NORTH_WEST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W; } else { - return Cell.NSWE_WEST; + return GeoStructure.CELL_FLAG_W; } } else // unchanged x { if (y > lastY) { - return Cell.NSWE_SOUTH; + return GeoStructure.CELL_FLAG_S; } else if (y < lastY) { - return Cell.NSWE_NORTH; + return GeoStructure.CELL_FLAG_N; } else { diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/config/GeoEngine.ini b/L2J_Mobius_8.0_Homunculus/dist/game/config/GeoEngine.ini index 609e8a89a7..e740c678dd 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_8.0_Homunculus/dist/game/config/GeoEngine.ini @@ -6,33 +6,44 @@ # at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ 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 + # ================================================================= -# Path finding +# Pathfinding # ================================================================= # 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 +# an alternative path (e.g. walk around obstacle), default: True +PathFinding = True -# Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 -PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 +# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 -# Weight for nodes without obstacles far from walls -LowWeight = 0.5 +# Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 +MoveWeight = 10 +MoveWeightDiag = 14 -# Weight for nodes near walls -MediumWeight = 2 +# 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 -# Weight for nodes with obstacles -HighWeight = 3 +# 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. -# Default: LowWeight * sqrt(2) -DiagonalWeight = 0.707 +# Maximum number of generated nodes per one path-finding process, default 3500 +MaxIterations = 3500 # ================================================================= -# Other +# Line of Sight # ================================================================= -# Correct player Z after falling through the ground. -CorrectPlayerZ = False +# 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 diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/data/geodata/L2D_GeoEngine.patch b/L2J_Mobius_8.0_Homunculus/dist/game/data/geodata/L2D_GeoEngine.patch deleted file mode 100644 index f8577e3a0e..0000000000 --- a/L2J_Mobius_8.0_Homunculus/dist/game/data/geodata/L2D_GeoEngine.patch +++ /dev/null @@ -1,6035 +0,0 @@ -Index: dist/game/GeoDataConverter.bat -=================================================================== ---- dist/game/GeoDataConverter.bat (nonexistent) -+++ dist/game/GeoDataConverter.bat (working copy) -@@ -0,0 +1,6 @@ -+@echo off -+title L2D geodata converter -+ -+java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter -+ -+pause -Index: dist/game/GeoDataConverter.sh -=================================================================== ---- dist/game/GeoDataConverter.sh (nonexistent) -+++ dist/game/GeoDataConverter.sh (working copy) -@@ -0,0 +1,4 @@ -+#! /bin/sh -+ -+java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 -+ -Index: dist/game/config/GeoEngine.ini -=================================================================== ---- dist/game/config/GeoEngine.ini (revision 8409) -+++ dist/game/config/GeoEngine.ini (working copy) -@@ -1,6 +1,10 @@ - # ================================================================= - # Geodata - # ================================================================= -+# Because of real-time performance we are using geodata files only in -+# diagonal L2D format now (using filename e.g. 22_16.l2d). -+# L2D geodata can be obtained by conversion of existing L2J or L2OFF geodata. -+# Launch "GeoDataConverter.bat/sh" and follow instructions to start the conversion. - - # 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/ -@@ -13,6 +17,16 @@ - CoordSynchronize = 2 - - # ================================================================= -+# Path checking -+# ================================================================= -+ -+# 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 finding - # ================================================================= - -@@ -23,19 +37,23 @@ - # Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - --# Weight for nodes without obstacles far from walls --LowWeight = 0.5 -+# Base path weight, when moving from one node to another on axis direction, default: 10 -+BaseWeight = 10 - --# Weight for nodes near walls --MediumWeight = 2 -+# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -+DiagonalWeight = 14 - --# Weight for nodes with obstacles --HighWeight = 3 -+# When movement flags of target node is blocked to any direction, multiply movement weight by this multiplier. -+# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 10 -+ObstacleMultiplier = 10 - --# Weight for diagonal movement. --# Default: LowWeight * sqrt(2) --DiagonalWeight = 0.707 -+# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 20 -+# For proper function must be higher than BaseWeight and/or DiagonalWeight. -+HeuristicWeight = 20 - -+# Maximum number of generated nodes per one path-finding process, default 3500 -+MaxIterations = 3500 -+ - # ================================================================= - # Other - # ================================================================= -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (working copy) -@@ -55,9 +55,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -@@ -73,9 +72,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (working copy) -@@ -18,11 +18,11 @@ - - import java.util.List; - --import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -+import org.l2jmobius.gameserver.geoengine.GeoEngine; - import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -+import org.l2jmobius.gameserver.network.SystemMessageId; - import org.l2jmobius.gameserver.util.BuilderUtil; - - public class AdminPathNode implements IAdminCommandHandler -@@ -37,29 +37,30 @@ - { - if (command.equals("admin_path_find")) - { -- if (!Config.PATHFINDING) -- { -- BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); -- return true; -- } - if (activeChar.getTarget() != null) - { -- final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); -+ final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); - if (path == null) - { -- BuilderUtil.sendSysMessage(activeChar, "No Route!"); -- return true; -+ BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); - } -- for (AbstractNodeLoc a : path) -+ else - { -- BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); -+ for (Location point : path) -+ { -+ BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); -+ } - } - } - else - { -- BuilderUtil.sendSysMessage(activeChar, "No Target!"); -+ activeChar.sendPacket(SystemMessageId.INVALID_TARGET); - } - } -+ else -+ { -+ return false; -+ } - return true; - } - -Index: java/org/l2jmobius/Config.java -=================================================================== ---- java/org/l2jmobius/Config.java (revision 8409) -+++ java/org/l2jmobius/Config.java (working copy) -@@ -32,7 +32,6 @@ - import java.net.UnknownHostException; - import java.nio.charset.StandardCharsets; - import java.nio.file.Files; --import java.nio.file.Path; - import java.nio.file.Paths; - import java.time.Duration; - import java.util.ArrayList; -@@ -966,14 +965,17 @@ - // -------------------------------------------------- - // GeoEngine - // -------------------------------------------------- -- public static Path GEODATA_PATH; -+ public static String GEODATA_PATH; -+ public static int COORD_SYNCHRONIZE; -+ public static int PART_OF_CHARACTER_HEIGHT; -+ public static int MAX_OBSTACLE_HEIGHT; - public static boolean PATHFINDING; - public static String PATHFIND_BUFFERS; -- public static float LOW_WEIGHT; -- public static float MEDIUM_WEIGHT; -- public static float HIGH_WEIGHT; -- public static float DIAGONAL_WEIGHT; -- public static int COORD_SYNCHRONIZE; -+ public static int BASE_WEIGHT; -+ public static int DIAGONAL_WEIGHT; -+ public static int HEURISTIC_WEIGHT; -+ public static int OBSTACLE_MULTIPLIER; -+ public static int MAX_ITERATIONS; - public static boolean CORRECT_PLAYER_Z; - - /** Attribute System */ -@@ -2568,14 +2570,17 @@ - - // Load GeoEngine config file (if exists) - final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); -- GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); -+ GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); -+ COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); -+ MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); - PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -- LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); -- MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); -- HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); -- DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); -- COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); -+ DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); -+ OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); -+ HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); -+ MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); - CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); - - // Load AllowedPlayerRaces config file (if exists) -Index: java/org/l2jmobius/gameserver/geoengine/GeoEngine.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (revision 8435) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (working copy) -@@ -16,100 +16,90 @@ - */ - package org.l2jmobius.gameserver.geoengine; - --import java.io.IOException; -+import java.io.File; - import java.io.RandomAccessFile; - import java.nio.ByteOrder; --import java.nio.channels.FileChannel.MapMode; --import java.nio.file.Files; --import java.nio.file.Path; --import java.util.concurrent.atomic.AtomicReferenceArray; --import java.util.logging.Level; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.List; - import java.util.logging.Logger; - - import org.l2jmobius.Config; - import org.l2jmobius.gameserver.data.xml.DoorData; - import org.l2jmobius.gameserver.data.xml.FenceData; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; --import org.l2jmobius.gameserver.geoengine.geodata.IRegion; --import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; --import org.l2jmobius.gameserver.geoengine.geodata.Region; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.World; - import org.l2jmobius.gameserver.model.WorldObject; -+import org.l2jmobius.gameserver.model.actor.Creature; - import org.l2jmobius.gameserver.model.instancezone.Instance; --import org.l2jmobius.gameserver.model.interfaces.ILocational; --import org.l2jmobius.gameserver.util.GeoUtils; --import org.l2jmobius.gameserver.util.LinePointIterator; --import org.l2jmobius.gameserver.util.LinePointIterator3D; -+import org.l2jmobius.gameserver.util.MathUtil; - - /** -- * @author -Nemesiss-, HorridoJoho -+ * @author Hasha - */ - public class GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); -+ protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - -- private static final int WORLD_MIN_X = -655360; -- private static final int WORLD_MIN_Y = -589824; -- private static final int WORLD_MIN_Z = -16384; -+ private final ABlock[][] _blocks; -+ private final BlockNull _nullBlock; - -- /** 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; -- -- /** The regions array */ -- private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); -- -- private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; -- private static final int ELEVATED_SEE_OVER_DISTANCE = 2; -- private static final int MAX_SEE_OVER_HEIGHT = 48; -- -- protected GeoEngine() -+ /** -+ * GeoEngine constructor. Loads all geodata files of chosen geodata format. -+ */ -+ public GeoEngine() - { - LOGGER.info("GeoEngine: Initializing..."); -- for (int i = 0; i < _regions.length(); i++) -- { -- _regions.set(i, NullRegion.INSTANCE); -- } - -+ // initialize block container -+ _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; -+ -+ // load null block -+ _nullBlock = new BlockNull(); -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files according to geoengine config setup - int loaded = 0; -- try -+ long fileSize = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { -- for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { -- for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) -+ final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); -+ if (f.exists() && !f.isDirectory()) - { -- final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); -- if (Files.exists(geoFilePath)) -+ // region file is load-able, try to load it -+ if (loadGeoBlocks(rx, ry)) - { -- try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) -- { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -- loaded++; -- } -+ loaded++; -+ fileSize += f.length(); - } - } -+ else -+ { -+ // region file is not load-able, load null blocks -+ loadNullBlocks(rx, ry); -+ } - } - } -- catch (Exception e) -+ -+ LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -+ if (loaded > 0) - { -- LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); -- System.exit(1); -+ LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); - } - -- LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -- -- // Avoid wrong configs when no files are loaded. -+ // avoid wrong configs when no files are loaded - if (loaded == 0) - { - if (Config.PATHFINDING) -@@ -123,619 +113,820 @@ - LOGGER.info("GeoEngine: Forcing CoordSynchronize setting to -1."); - } - } -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); - } - - /** -- * @param geoX -- * @param geoY -- * @return the region -+ * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. -+ * @return boolean : True, when geodata file was loaded without problem. - */ -- private IRegion getRegion(int geoX, int geoY) -+ private final boolean loadGeoBlocks(int regionX, int regionY) - { -- final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); -- if ((region < 0) || (region >= _regions.length())) -+ final String filename = String.format(GeoFormat.L2D.getFilename(), regionX, regionY); -+ -+ // standard load -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) - { -- return null; -+ // initialize file buffer -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, GeoFormat.L2D); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ } -+ -+ // check data consistency -+ if (buffer.remaining() > 0) -+ { -+ LOGGER.warning("GeoEngine: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ } -+ -+ // loading was successful -+ return true; - } -- return _regions.get(region); -- } -- -- /** -- * @param filePath -- * @param regionX -- * @param regionY -- * @throws IOException -- */ -- 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")) -+ catch (Exception e) - { -- _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -+ // an error occured while loading, load null blocks -+ LOGGER.warning("GeoEngine: Error while loading " + filename + " region file."); -+ LOGGER.warning(e.getMessage()); -+ -+ // replace whole region file with null blocks -+ loadNullBlocks(regionX, regionY); -+ -+ // loading was not successful -+ return false; - } - } - - /** -- * @param regionX -- * @param regionY -+ * Loads null blocks. Used when no region file is detected or an error occurs during loading. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. - */ -- public void unloadRegion(int regionX, int regionY) -+ private final void loadNullBlocks(int regionX, int regionY) - { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); -- } -- -- /** -- * @param geoX -- * @param geoY -- * @return if geodata exist -- */ -- public boolean hasGeoPos(int geoX, int geoY) -- { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // load all null blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { -- return false; -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[blockX + ix][blockY + iy] = _nullBlock; -+ } - } -- return region.hasGeo(); - } - -+ // GEODATA - GENERAL -+ - /** -- * Checks the specified position for available geodata. -- * @param x the world x -- * @param y the world y -- * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise -+ * Converts world X to geodata X. -+ * @param worldX -+ * @return int : Geo X - */ -- public boolean hasGeo(int x, int y) -+ public static int getGeoX(int worldX) - { -- return hasGeoPos(getGeoX(x), getGeoY(y)); -+ return (MathUtil.limit(worldX, World.MAP_MIN_X, World.MAP_MAX_X) - World.MAP_MIN_X) >> 4; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe check -+ * Converts world Y to geodata Y. -+ * @param worldY -+ * @return int : Geo Y - */ -- public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) -+ public static int getGeoY(int worldY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return true; -- } -- return region.checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(worldY, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.MAP_MIN_Y) >> 4; - } - - /** -+ * Converts geodata X to world X. - * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe anti-corner cut -+ * @return int : World X - */ -- public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) -+ public static int getWorldX(int geoX) - { -- boolean can = true; -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- return can && checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(geoX, 0, GeoStructure.GEO_CELLS_X) << 4) + World.MAP_MIN_X + 8; - } - - /** -- * @param geoX -+ * Converts geodata Y to world Y. - * @param geoY -- * @param worldZ -- * @return the nearest Z value -+ * @return int : World Y - */ -- public int getNearestZ(int geoX, int geoY, int worldZ) -+ public static int getWorldY(int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return worldZ; -- } -- return region.getNearestZ(geoX, geoY, worldZ); -+ return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next lower Z value -+ * Returns block of geodata on given coordinates. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return {@link ABlock} : Block of geodata. - */ -- public int getNextLowerZ(int geoX, int geoY, int worldZ) -+ private final ABlock getBlock(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final int x = geoX / GeoStructure.BLOCK_CELLS_X; -+ final int y = geoY / GeoStructure.BLOCK_CELLS_Y; -+ -+ // if x or y is out of array return null -+ if ((x > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) - { -- return worldZ; -+ return _blocks[x][y]; - } -- return region.getNextLowerZ(geoX, geoY, worldZ); -+ return null; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next higher Z value -+ * Check if geo coordinates has geo. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return boolean : True, if given geo coordinates have geodata - */ -- public int getNextHigherZ(int geoX, int geoY, int worldZ) -+ public boolean hasGeoPos(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final ABlock block = getBlock(geoX, geoY); -+ if (block == null) // null block check - { -- return worldZ; -+ // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) -+ // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); -+ return false; - } -- return region.getNextHigherZ(geoX, geoY, worldZ); -+ return block.hasGeoPos(); - } - - /** -- * Gets the Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the height of cell, which is closest to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, closest to given coordinates. - */ -- public int getHeight(int x, int y, int z) -+ public short getHeightNearest(int geoX, int geoY, int worldZ) - { -- return getNearestZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getHeightNearest(geoX, geoY, worldZ) : (short) worldZ; - } - - /** -- * Gets the next lower Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the NSWE flag byte of cell, which is closes to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. - */ -- public int getLowerHeight(int x, int y, int z) -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) - { -- return getNextLowerZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getNsweNearest(geoX, geoY, worldZ) : (byte) 0xFF; - } - - /** -- * Gets the next higher Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Check if world coordinates has geo. -+ * @param worldX : World X -+ * @param worldY : World Y -+ * @return boolean : True, if given world coordinates have geodata - */ -- public int getHigherHeight(int x, int y, int z) -+ public boolean hasGeo(int worldX, int worldY) - { -- return getNextHigherZ(getGeoX(x), getGeoY(y), z); -+ return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); - } - - /** -- * @param worldX -- * @return the geo X -+ * Returns closest Z coordinate according to geodata. -+ * @param worldX : world x -+ * @param worldY : world y -+ * @param worldZ : world z -+ * @return short : nearest Z coordinates according to geodata - */ -- public int getGeoX(int worldX) -+ public short getHeight(int worldX, int worldY, int worldZ) - { -- return (worldX - WORLD_MIN_X) / 16; -+ return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - -- /** -- * @param worldY -- * @return the geo Y -- */ -- public int getGeoY(int worldY) -- { -- return (worldY - WORLD_MIN_Y) / 16; -- } -+ // PATHFINDING - - /** -- * @param worldZ -- * @return the geo Z -+ * Check line of sight from {@link WorldObject} to {@link WorldObject}. -+ * @param origin : The origin object. -+ * @param target : The target object. -+ * @return {@code boolean} : True if origin can see target - */ -- public int getGeoZ(int worldZ) -+ public boolean canSeeTarget(WorldObject origin, WorldObject target) - { -- return (worldZ - WORLD_MIN_Z) / 16; -- } -- -- /** -- * @param geoX -- * @return the world X -- */ -- public int getWorldX(int geoX) -- { -- return (geoX * 16) + WORLD_MIN_X + 8; -- } -- -- /** -- * @param geoY -- * @return the world Y -- */ -- public int getWorldY(int geoY) -- { -- return (geoY * 16) + WORLD_MIN_Y + 8; -- } -- -- /** -- * @param geoZ -- * @return the world Z -- */ -- public int getWorldZ(int geoZ) -- { -- return (geoZ * 16) + WORLD_MIN_Z + 8; -- } -- -- /** -- * Can see target. Doors as target always return true. Checks doors between. -- * @param cha the character -- * @param target the target -- * @return {@code true} if the character can see the target (LOS), {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, WorldObject target) -- { -- if (target.isDoor()) -+ if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) - { -- // Can always see doors. - return true; - } -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param cha the character -- * @param worldPosition the world position -- * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) -- { -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param world -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @param tworld the target's instanceId -- * @return -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) -- { -- return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instance -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) -- { -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) -+ -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = target.getX(); -+ final int ty = target.getY(); -+ final int tz = target.getZ(); -+ -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } -- return canSeeTarget(x, y, z, tx, ty, tz); -- } -- -- /** -- * @param prevX -- * @param prevY -- * @param prevGeoZ -- * @param curX -- * @param curY -- * @param nswe -- * @return the LOS Z value -- */ -- private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) -- { -- if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { -- throw new RuntimeException("Multiple directions!"); -+ return false; - } - -- if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- return getNearestZ(curX, curY, prevGeoZ); -+ return true; - } - -- return getNextHigherZ(curX, curY, prevGeoZ); -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) -+ { -+ return true; -+ } -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) -+ { -+ return goz == gtz; -+ } -+ -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) -+ { -+ oheight = ((Creature) origin).getCollisionHeight() * 2; -+ } -+ -+ double theight = 0; -+ if (target.isCreature()) -+ { -+ theight = ((Creature) target).getCollisionHeight() * 2; -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); - } - - /** -- * Can see target. Does not check doors between. -- * @param xValue the x coordinate -- * @param yValue the y coordinate -- * @param zValue the z coordinate -- * @param txValue the target's x coordinate -- * @param tyValue the target's y coordinate -- * @param tzValue the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -+ * Check line of sight from {@link WorldObject} to {@link Location}. -+ * @param origin : The origin object. -+ * @param position : The target position. -+ * @return {@code boolean} : True if object can see position - */ -- public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) -+ public boolean canSeeTarget(WorldObject origin, Location position) - { -- int x = xValue; -- int y = yValue; -- int tx = txValue; -- int ty = tyValue; -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = position.getX(); -+ final int ty = position.getY(); -+ final int tz = position.getZ(); - -- int geoX = getGeoX(x); -- int geoY = getGeoY(y); -- int tGeoX = getGeoX(tx); -- int tGeoY = getGeoY(ty); -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) -+ { -+ return false; -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) -+ { -+ return false; -+ } - -- int z = getNearestZ(geoX, geoY, zValue); -- int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- // Fastpath. -- if ((geoX == tGeoX) && (geoY == tGeoY)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- if (hasGeoPos(tGeoX, tGeoY)) -- { -- return z == tz; -- } - return true; - } - -- if (tz > z) -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) - { -- int tmp = tx; -- tx = x; -- x = tmp; -- -- tmp = ty; -- ty = y; -- y = tmp; -- -- tmp = tz; -- tz = z; -- z = tmp; -- -- tmp = tGeoX; -- tGeoX = geoX; -- geoX = tmp; -- -- tmp = tGeoY; -- tGeoY = geoY; -- geoY = tmp; -+ return goz == gtz; - } - -- final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); -- // First point is guaranteed to be available, skip it, we can always see our own position. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- final int prevZ = pointIter.z(); -- int prevGeoZ = prevZ; -- int ptIndex = 0; -- while (pointIter.next()) -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight(); -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); -+ } -+ -+ /** -+ * Simple check for origin to target visibility. -+ * @param goxValue : origin X geodata coordinate -+ * @param goyValue : origin Y geodata coordinate -+ * @param gozValue : origin Z geodata coordinate -+ * @param oheight : origin height (if instance of {@link Character}) -+ * @param gtxValue : target X geodata coordinate -+ * @param gtyValue : target Y geodata coordinate -+ * @param gtzValue : target Z geodata coordinate -+ * @param theight : target height (if instance of {@link Character}) -+ * @param instance -+ * @return {@code boolean} : True, when target can be seen. -+ */ -+ private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) -+ { -+ int goz = gozValue; -+ int gtz = gtzValue; -+ int gox = goxValue; -+ int goy = goyValue; -+ int gtx = gtxValue; -+ int gty = gtyValue; -+ -+ // get line of sight Z coordinates -+ double losoz = goz + ((oheight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ double lostz = gtz + ((theight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ -+ // get X delta and signum -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirox = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; -+ final byte dirtx = sx > 0 ? GeoStructure.CELL_FLAG_W : GeoStructure.CELL_FLAG_E; -+ -+ // get Y delta and signum -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte diroy = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ final byte dirty = sy > 0 ? GeoStructure.CELL_FLAG_N : GeoStructure.CELL_FLAG_S; -+ -+ // get Z delta -+ final int dm = Math.max(dx, dy); -+ final double dz = (lostz - losoz) / dm; -+ -+ // get direction flag for diagonal movement -+ final byte diroxy = getDirXY(dirox, diroy); -+ final byte dirtxy = getDirXY(dirtx, dirty); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte diro; -+ byte dirt; -+ -+ // initialize node values -+ int nox = gox; -+ int noy = goy; -+ int ntx = gtx; -+ int nty = gty; -+ byte nsweo = getNsweNearest(gox, goy, goz); -+ byte nswet = getNsweNearest(gtx, gty, gtz); -+ -+ // loop -+ ABlock block; -+ int index; -+ for (int i = 0; i < ((dm + 1) / 2); i++) -+ { -+ // reset direction flag -+ diro = 0; -+ dirt = 0; - -- if ((curX == prevX) && (curY == prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- continue; -+ // calculate next point XY coordinates -+ d -= dy; -+ d += dx; -+ nox += sx; -+ ntx -= sx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroxy; -+ dirt |= dirtxy; - } -+ else if (e2 > -dy) -+ { -+ // calculate next point X coordinate -+ d -= dy; -+ nox += sx; -+ ntx -= sx; -+ diro |= dirox; -+ dirt |= dirtx; -+ } -+ else if (e2 < dx) -+ { -+ // calculate next point Y coordinate -+ d += dx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroy; -+ dirt |= dirty; -+ } - -- final int beeCurZ = pointIter.z(); -- int curGeoZ = prevGeoZ; -- -- // Check if the position has geodata. -- if (hasGeoPos(curX, curY)) - { -- final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); -- curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); -- int maxHeight; -- if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) -+ // get block of the next cell -+ block = getBlock(nox, noy); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nsweo & diro) == 0) - { -- maxHeight = z + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); - } - else - { -- maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); - } - -- boolean canSeeThrough = false; -- if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) -+ // layer does not exist, return -+ if (index == -1) - { -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else -- { -- canSeeThrough = true; -- } -+ return false; - } - -- if (!canSeeThrough) -+ // get layer and next line of sight Z coordinate -+ goz = block.getHeight(index); -+ losoz += dz; -+ -+ // perform line of sight check, return when fails -+ if ((goz - losoz) > Config.MAX_OBSTACLE_HEIGHT) - { - return false; - } -+ -+ // get layer nswe -+ nsweo = block.getNswe(index); - } -+ { -+ // get block of the next cell -+ block = getBlock(ntx, nty); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nswet & dirt) == 0) -+ { -+ index = block.getIndexAbove(ntx, nty, gtz - GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ else -+ { -+ index = block.getIndexBelow(ntx, nty, gtz + GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ -+ // layer does not exist, return -+ if (index == -1) -+ { -+ return false; -+ } -+ -+ // get layer and next line of sight Z coordinate -+ gtz = block.getHeight(index); -+ lostz -= dz; -+ -+ // perform line of sight check, return when fails -+ if ((gtz - lostz) > Config.MAX_OBSTACLE_HEIGHT) -+ { -+ return false; -+ } -+ -+ // get layer nswe -+ nswet = block.getNswe(index); -+ } - -- prevX = curX; -- prevY = curY; -- prevGeoZ = curGeoZ; -- ++ptIndex; -+ // update coords -+ gox = nox; -+ goy = noy; -+ gtx = ntx; -+ gty = nty; - } - -- return true; -+ // when iteration is completed, compare final Z coordinates -+ return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); - } - - /** -- * Move check. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param zValue the z coordinate -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tzValue the target's z coordinate -- * @param instance the instance -- * @return the last Location (x,y,z) where player can walk - just before wall -+ * Check movement from coordinates to coordinates. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {code boolean} : True if target coordinates are reachable from origin coordinates - */ -- public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) -+ public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- final int geoX = getGeoX(x); -- final int geoY = getGeoY(y); -- final int z = getNearestZ(geoX, geoY, zValue); -- final int tGeoX = getGeoX(tx); -- final int tGeoY = getGeoY(ty); -- final int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } -- if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = z; -+ // perform geodata check -+ final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); -+ return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); -+ } -+ -+ /** -+ * Check movement from origin to target. Returns last available point in the checked path. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {@link Location} : Last point where object can walk (just before wall) -+ */ -+ public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) -+ { -+ return new Location(ox, oy, oz); -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) -+ { -+ return new Location(ox, oy, oz); -+ } - -- while (pointIter.next()) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -- -- if (hasGeoPos(prevX, prevY)) -- { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -- { -- // Can't move, return previous location. -- return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); -- } -- } -- -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return new Location(tx, ty, tz); - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != tz)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- // Different floors, return start location. -- return new Location(x, y, z); -+ return new Location(tx, ty, tz); - } - -- return new Location(tx, ty, tz); -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) -+ { -+ return new Location(tx, ty, tz); -+ } -+ -+ // perform geodata check -+ return checkMove(gox, goy, goz, gtx, gty, gtz, instance); - } - - /** -- * Checks if its possible to move from one location to another. -- * @param fromX the X coordinate to start checking from -- * @param fromY the Y coordinate to start checking from -- * @param fromZvalue the Z coordinate to start checking from -- * @param toX the X coordinate to end checking at -- * @param toY the Y coordinate to end checking at -- * @param toZvalue the Z coordinate to end checking at -- * @param instance the instance -- * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise -+ * With this method you can check if a position is visible or can be reached by beeline movement.
-+ * Target X and Y reachable and Z is on same floor: -+ *
    -+ *
  • Location of the target with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y reachable but Z is on another floor: -+ *
    -+ *
  • Location of the origin with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y not reachable: -+ *
    -+ *
  • Last accessible location in destination to target.
  • -+ *
-+ * @param gox : origin X geodata coordinate -+ * @param goy : origin Y geodata coordinate -+ * @param goz : origin Z geodata coordinate -+ * @param gtx : target X geodata coordinate -+ * @param gty : target Y geodata coordinate -+ * @param gtz : target Z geodata coordinate -+ * @param instance -+ * @return {@link GeoLocation} : The last allowed point of movement. - */ -- public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) -+ protected final GeoLocation checkMove(int gox, int goy, int goz, int gtx, int gty, int gtz, Instance instance) - { -- final int geoX = getGeoX(fromX); -- final int geoY = getGeoY(fromY); -- final int fromZ = getNearestZ(geoX, geoY, fromZvalue); -- final int tGeoX = getGeoX(toX); -- final int tGeoY = getGeoY(toY); -- final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); -- -- if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) -+ if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } -- if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) -+ if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = fromZ; -+ // get X delta, signum and direction flag -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirX = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; - -- while (pointIter.next()) -+ // get Y delta, signum and direction flag -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte dirY = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ -+ // get direction flag for diagonal movement -+ final byte dirXY = getDirXY(dirX, dirY); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte direction; -+ -+ // load pointer coordinates -+ int gpx = gox; -+ int gpy = goy; -+ int gpz = goz; -+ -+ // load next pointer -+ int nx = gpx; -+ int ny = gpy; -+ -+ // loop -+ int count = 0; -+ while (count++ < Config.MAX_ITERATIONS) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -+ direction = 0; - -- if (hasGeoPos(prevX, prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -+ d -= dy; -+ d += dx; -+ nx += sx; -+ ny += sy; -+ direction |= dirXY; -+ } -+ else if (e2 > -dy) -+ { -+ d -= dy; -+ nx += sx; -+ direction |= dirX; -+ } -+ else if (e2 < dx) -+ { -+ d += dx; -+ ny += sy; -+ direction |= dirY; -+ } -+ -+ // obstacle found, return -+ if ((getNsweNearest(gpx, gpy, gpz) & direction) == 0) -+ { -+ return new GeoLocation(gpx, gpy, gpz); -+ } -+ -+ // update pointer coordinates -+ gpx = nx; -+ gpy = ny; -+ gpz = getHeightNearest(nx, ny, gpz); -+ -+ // target coordinates reached -+ if ((gpx == gtx) && (gpy == gty)) -+ { -+ if (gpz == gtz) - { -- return false; -+ // path found, Z coordinates are okay, return target point -+ return new GeoLocation(gtx, gty, gtz); - } -+ -+ // path found, Z coordinates are not okay, return last good point -+ return new GeoLocation(gpx, gpy, gpz); - } -+ } -+ -+ return new GeoLocation(gox, goy, goz); -+ } -+ -+ /** -+ * Returns diagonal NSWE flag format of combined two NSWE flags. -+ * @param dirX : X direction NSWE flag -+ * @param dirY : Y direction NSWE flag -+ * @return byte : NSWE flag of combined direction -+ */ -+ private static byte getDirXY(byte dirX, byte dirY) -+ { -+ // check axis directions -+ if (dirY == GeoStructure.CELL_FLAG_N) -+ { -+ if (dirX == GeoStructure.CELL_FLAG_W) -+ { -+ return GeoStructure.CELL_FLAG_NW; -+ } - -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return GeoStructure.CELL_FLAG_NE; - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) -+ if (dirX == GeoStructure.CELL_FLAG_W) - { -- // Different floors. -- return false; -+ return GeoStructure.CELL_FLAG_SW; - } - -- return true; -+ return GeoStructure.CELL_FLAG_SE; - } - -+ /** -+ * Returns the list of location objects as a result of complete path calculation. -+ * @param ox : origin x -+ * @param oy : origin y -+ * @param oz : origin z -+ * @param tx : target x -+ * @param ty : target y -+ * @param tz : target z -+ * @param instance -+ * @return {@code List} : complete path from nodes -+ */ -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ return null; -+ } -+ -+ /** -+ * Returns the instance of the {@link GeoEngine}. -+ * @return {@link GeoEngine} : The instance. -+ */ - public static GeoEngine getInstance() - { - return SingletonHolder.INSTANCE; -@@ -743,6 +934,6 @@ - - private static class SingletonHolder - { -- protected static final GeoEngine INSTANCE = new GeoEngine(); -+ protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); - } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (working copy) -@@ -20,88 +20,84 @@ - import java.util.LinkedList; - import java.util.List; - import java.util.ListIterator; --import java.util.logging.Level; --import java.util.logging.Logger; - - import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; --import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; --import org.l2jmobius.gameserver.model.World; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -+import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.instancezone.Instance; - - /** -- * @author -Nemesiss- -+ * @author Hasha - */ --public class GeoEnginePathfinding -+final class GeoEnginePathfinding extends GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); -+ // pre-allocated buffers -+ private final BufferHolder[] _buffers; - -- private BufferInfo[] _buffers; -- - protected GeoEnginePathfinding() - { -- try -+ super(); -+ -+ final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ _buffers = new BufferHolder[array.length]; -+ int count = 0; -+ for (int i = 0; i < array.length; i++) - { -- final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ final String buf = array[i]; -+ final String[] args = buf.split("x"); - -- _buffers = new BufferInfo[array.length]; -- -- String buf; -- String[] args; -- for (int i = 0; i < array.length; i++) -+ try - { -- buf = array[i]; -- args = buf.split("x"); -- if (args.length != 2) -- { -- throw new Exception("Invalid buffer definition: " + buf); -- } -- -- _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); -+ final int size = Integer.parseInt(args[1]); -+ count += size; -+ _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); - } -+ catch (Exception e) -+ { -+ LOGGER.warning("GeoEnginePathfinding: Can not load buffer setting: " + buf); -+ } - } -- catch (Exception e) -- { -- LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); -- throw new Error("CellPathFinding: load aborted"); -- } -+ -+ LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); - } - -- public boolean pathNodesExist(short regionoffset) -+ @Override -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- return false; -- } -- -- public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) -- { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -- if (!GeoEngine.getInstance().hasGeo(x, y)) -+ // get origin and check existing geo coords -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { - 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)) -+ -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coords -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { - 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)))); -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // Prepare buffer for pathfinding calculations -+ final NodeBuffer buffer = getBuffer(64 + (2 * Math.max(Math.abs(gox - gtx), Math.abs(goy - gty)))); - if (buffer == null) - { - return null; - } - -- List path = null; -+ // find path -+ List path = null; - try - { -- final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); -- -+ final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); - if (result == null) - { - return null; -@@ -125,33 +121,45 @@ - return path; - } - -- int currentX, currentY, currentZ; -- ListIterator middlePoint; -+ // get path list iterator -+ final ListIterator point = path.listIterator(); - -- middlePoint = path.listIterator(); -- currentX = x; -- currentY = y; -- currentZ = z; -+ // get node A (origin) -+ int nodeAx = gox; -+ int nodeAy = goy; -+ short nodeAz = goz; - -- while (middlePoint.hasNext()) -+ // get node B -+ GeoLocation nodeB = (GeoLocation) point.next(); -+ -+ // iterate thought the path to optimize it -+ int count = 0; -+ while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) - { -- final AbstractNodeLoc locMiddle = middlePoint.next(); -- if (!middlePoint.hasNext()) -- { -- break; -- } -+ // get node C -+ final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - -- final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); -- if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) -+ // check movement from node A to node C -+ final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); -+ if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) - { -- middlePoint.remove(); -+ // can move from node A to node C -+ -+ // remove node B -+ point.remove(); - } - else - { -- currentX = locMiddle.getX(); -- currentY = locMiddle.getY(); -- currentZ = locMiddle.getZ(); -+ // can not move from node A to node C -+ -+ // set node A (node B is part of path, update A coordinates) -+ nodeAx = nodeB.getGeoX(); -+ nodeAy = nodeB.getGeoY(); -+ nodeAz = (short) nodeB.getZ(); - } -+ -+ // set node B -+ nodeB = (GeoLocation) point.next(); - } - - return path; -@@ -158,144 +166,102 @@ - } - - /** -- * Convert geodata position to pathnode position -- * @param geo_pos -- * @return pathnode position -+ * Create list of node locations as result of calculated buffer node tree. -+ * @param node : the entry point -+ * @return List : list of node location - */ -- public short getNodePos(int geo_pos) -+ private static List constructPath(Node node) - { -- return (short) (geo_pos >> 3); // OK? -- } -- -- /** -- * Convert node position to pathnode block position -- * @param node_pos -- * @return pathnode block position (0...255) -- */ -- public short getNodeBlock(int node_pos) -- { -- return (short) (node_pos % 256); -- } -- -- public byte getRegionX(int node_pos) -- { -- return (byte) ((node_pos >> 8) + World.TILE_X_MIN); -- } -- -- public byte getRegionY(int node_pos) -- { -- return (byte) ((node_pos >> 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 node_x rx -- * @return -- */ -- public int calculateWorldX(short node_x) -- { -- return World.MAP_MIN_X + (node_x * 128) + 48; -- } -- -- /** -- * Convert pathnode y to World y position -- * @param node_y -- * @return -- */ -- public int calculateWorldY(short node_y) -- { -- return World.MAP_MIN_Y + (node_y * 128) + 48; -- } -- -- private List constructPath(AbstractNode nodeValue) -- { -- final LinkedList path = new LinkedList<>(); -- int previousDirectionX = Integer.MIN_VALUE; -- int previousDirectionY = Integer.MIN_VALUE; -- int directionX, directionY; -+ // create empty list -+ final LinkedList list = new LinkedList<>(); - -- AbstractNode node = nodeValue; -- while (node.getParent() != null) -+ // set direction X/Y -+ int dx = 0; -+ int dy = 0; -+ -+ // get target parent -+ Node target = node; -+ Node parent = target.getParent(); -+ -+ // while parent exists -+ int count = 0; -+ while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { -- directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); -- directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); -+ // get parent <> target direction X/Y -+ final int nx = parent.getLoc().getGeoX() - target.getLoc().getGeoX(); -+ final int ny = parent.getLoc().getGeoY() - target.getLoc().getGeoY(); - -- // only add a new route point if moving direction changes -- if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) -+ // direction has changed? -+ if ((dx != nx) || (dy != ny)) - { -- previousDirectionX = directionX; -- previousDirectionY = directionY; -+ // add node to the beginning of the list -+ list.addFirst(target.getLoc()); - -- path.addFirst(node.getLoc()); -- node.setLoc(null); -+ // update direction X/Y -+ dx = nx; -+ dy = ny; - } - -- node = node.getParent(); -+ // move to next node, set target and get its parent -+ target = parent; -+ parent = target.getParent(); - } - -- return path; -+ // return list -+ return list; - } - -- private CellNodeBuffer alloc(int size) -+ /** -+ * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer. -+ * @param size : pre-calculated minimal required size -+ * @return NodeBuffer : buffer -+ */ -+ private final NodeBuffer getBuffer(int size) - { -- CellNodeBuffer current = null; -- for (BufferInfo i : _buffers) -+ NodeBuffer current = null; -+ for (BufferHolder holder : _buffers) - { -- if (i.mapSize >= size) -+ // Find proper size of buffer -+ if (holder._size < size) - { -- for (CellNodeBuffer buf : i.buffer) -+ continue; -+ } -+ -+ // Find unlocked NodeBuffer -+ for (NodeBuffer buffer : holder._buffer) -+ { -+ if (!buffer.isLocked()) - { -- if (buf.lock()) -- { -- current = buf; -- break; -- } -+ continue; - } -- if (current != null) -- { -- break; -- } - -- // not found, allocate temporary buffer -- current = new CellNodeBuffer(i.mapSize); -- current.lock(); -- if (i.buffer.size() < i.count) -- { -- i.buffer.add(current); -- break; -- } -+ return buffer; - } -+ -+ // NodeBuffer not found, allocate temporary buffer -+ current = new NodeBuffer(holder._size); -+ current.isLocked(); - } - - return current; - } - -- private static final class BufferInfo -+ /** -+ * NodeBuffer container with specified size and count of separate buffers. -+ */ -+ private static final class BufferHolder - { -- final int mapSize; -- final int count; -- ArrayList buffer; -+ final int _size; -+ List _buffer; - -- public BufferInfo(int size, int cnt) -+ public BufferHolder(int size, int count) - { -- mapSize = size; -- count = cnt; -- buffer = new ArrayList<>(count); -+ _size = size; -+ _buffer = new ArrayList<>(count); -+ for (int i = 0; i < count; i++) -+ { -+ _buffer.add(new NodeBuffer(size)); -+ } - } - } -- -- public static GeoEnginePathfinding getInstance() -- { -- return SingletonHolder.INSTANCE; -- } -- -- private static class SingletonHolder -- { -- protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); -- } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (working copy) -@@ -0,0 +1,197 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @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 height of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getHeightNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first above given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, above given coordinates. -+ */ -+ public abstract short getHeightAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first below given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is closest to given coordinates. -+ * @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 the NSWE flag byte of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getNsweNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first above given coordinates. -+ * @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 getNsweAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first below given coordinates. -+ * @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 getNsweBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is closes layer to given coordinates. -+ * @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. -+ * @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 above given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is first layer below given coordinates. -+ * @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 index to data of the cell, which is first layer below given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexBelowOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeight(int index); -+ -+ /** -+ * Returns the height of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightOriginal(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNswe(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNsweOriginal(int index); -+ -+ /** -+ * Sets the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @param nswe : New NSWE flag byte. -+ */ -+ public abstract void setNswe(int index, byte nswe); -+ -+ /** -+ * Saves the block in L2D format to {@link BufferedOutputStream}. Used only for L2D geodata conversion. -+ * @param stream : The stream. -+ * @throws IOException : Can't save the block to steam. -+ */ -+ public abstract void saveBlock(BufferedOutputStream stream) throws IOException; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (working copy) -@@ -0,0 +1,252 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockComplex(ByteBuffer bb, GeoFormat format) -+ { -+ // initialize buffer -+ _buffer = new byte[GeoStructure.BLOCK_CELLS * 3]; -+ -+ // load data -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // 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); -+ } -+ else -+ { -+ // get nswe -+ final byte nswe = bb.get(); -+ _buffer[i * 3] = nswe; -+ -+ // get height -+ final short height = bb.getShort(); -+ _buffer[(i * 3) + 1] = (byte) (height & 0x00FF); -+ _buffer[(i * 3) + 2] = (byte) (height >> 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height > worldZ ? height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height < worldZ ? height : Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public 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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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 int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_COMPLEX_L2D); -+ -+ // write block data -+ stream.write(_buffer, 0, GeoStructure.BLOCK_CELLS * 3); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (working copy) -@@ -0,0 +1,176 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockFlat extends ABlock -+{ -+ protected final short _height; -+ protected byte _nswe; -+ -+ /** -+ * Creates FlatBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockFlat(ByteBuffer bb, GeoFormat format) -+ { -+ _height = bb.getShort(); -+ _nswe = format != GeoFormat.L2D ? 0x0F : (byte) (0xFF); -+ if (format == GeoFormat.L2OFF) -+ { -+ bb.getShort(); -+ } -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return true; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height > worldZ ? _height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height < worldZ ? _height : Short.MAX_VALUE; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height > worldZ ? _nswe : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height < worldZ ? _nswe : 0; -+ } -+ -+ @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return index -+ return _height < worldZ ? 0 : -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _nswe = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_FLAT_L2D); -+ -+ // write height -+ stream.write((byte) (_height & 0x00FF)); -+ stream.write((byte) (_height >> 8)); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (working copy) -@@ -0,0 +1,465 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+import java.nio.ByteOrder; -+import java.util.Arrays; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockMultilayer extends ABlock -+{ -+ private static final int MAX_LAYERS = Byte.MAX_VALUE; -+ -+ private static ByteBuffer _temp; -+ -+ /** -+ * Initializes the temporarily buffer. -+ */ -+ public static void initialize() -+ { -+ // initialize temporarily buffer and sorting mechanism -+ _temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3); -+ _temp.order(ByteOrder.LITTLE_ENDIAN); -+ } -+ -+ /** -+ * Releases temporarily buffer. -+ */ -+ public static void release() -+ { -+ _temp = null; -+ } -+ -+ protected byte[] _buffer; -+ -+ /** -+ * Implicit constructor for children class. -+ */ -+ protected BlockMultilayer() -+ { -+ _buffer = null; -+ } -+ -+ /** -+ * Creates MultilayerBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockMultilayer(ByteBuffer bb, GeoFormat format) -+ { -+ // 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 = format != GeoFormat.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++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // get data -+ final short data = bb.getShort(); -+ -+ // add nswe and height -+ _temp.put((byte) (data & 0x000F)); -+ _temp.putShort((short) ((short) (data & 0xFFF0) >> 1)); -+ } -+ else -+ { -+ // add nswe -+ _temp.put(bb.get()); -+ -+ // add height -+ _temp.putShort(bb.getShort()); -+ } -+ } -+ } -+ -+ // 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 height -+ if (height > worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, return minimum value -+ return Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 height -+ if (height < worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, return maximum value -+ return Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 nswe -+ if (height > worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 nswe -+ if (height < worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @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 first layer data (first from bottom) -+ byte layers = _buffer[index++]; -+ -+ // loop though all cell layers, find closest layer -+ 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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ // get height -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(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]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ // get nswe -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ // set nswe -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_MULTILAYER_L2D); -+ -+ // for each cell -+ int index = 0; -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ // write layers count -+ final byte layers = _buffer[index++]; -+ stream.write(layers); -+ -+ // write cell data -+ stream.write(_buffer, index, layers * 3); -+ -+ // move index to next cell -+ index += layers * 3; -+ } -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (working copy) -@@ -0,0 +1,150 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockNull extends ABlock -+{ -+ private final byte _nswe; -+ -+ public BlockNull() -+ { -+ _nswe = (byte) 0xFF; -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return false; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweBelow(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) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) -+ { -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (nonexistent) -@@ -1,48 +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 . -- */ --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() -- { -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (nonexistent) -@@ -1,77 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (nonexistent) -@@ -1,56 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (working copy) -@@ -0,0 +1,39 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public enum GeoFormat -+{ -+ L2J("%d_%d.l2j"), -+ L2OFF("%d_%d_conv.dat"), -+ L2D("%d_%d.l2d"); -+ -+ private final String _filename; -+ -+ private GeoFormat(String filename) -+ { -+ _filename = filename; -+ } -+ -+ public String getFilename() -+ { -+ return _filename; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (working copy) -@@ -0,0 +1,67 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.geoengine.GeoEngine; -+import org.l2jmobius.gameserver.model.Location; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoLocation extends Location -+{ -+ private byte _nswe; -+ -+ public GeoLocation(int x, int y, int z) -+ { -+ super(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public void set(int x, int y, short z) -+ { -+ super.setXYZ(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public int getGeoX() -+ { -+ return _x; -+ } -+ -+ public int getGeoY() -+ { -+ return _y; -+ } -+ -+ @Override -+ public int getX() -+ { -+ return GeoEngine.getWorldX(_x); -+ } -+ -+ @Override -+ public int getY() -+ { -+ return GeoEngine.getWorldY(_y); -+ } -+ -+ public byte getNSWE() -+ { -+ return _nswe; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (working copy) -@@ -0,0 +1,70 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoStructure -+{ -+ // cells -+ public static final byte CELL_FLAG_E = 1 << 0; -+ public static final byte CELL_FLAG_W = 1 << 1; -+ public static final byte CELL_FLAG_S = 1 << 2; -+ public static final byte CELL_FLAG_N = 1 << 3; -+ public static final byte CELL_FLAG_SE = 1 << 4; -+ public static final byte CELL_FLAG_SW = 1 << 5; -+ public static final byte CELL_FLAG_NE = 1 << 6; -+ public static final byte CELL_FLAG_NW = (byte) (1 << 7); -+ -+ public static final int CELL_HEIGHT = 8; -+ public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6; -+ -+ // blocks -+ public static final byte TYPE_FLAT_L2J_L2OFF = 0; -+ public static final byte TYPE_FLAT_L2D = (byte) 0xD0; -+ public static final byte TYPE_COMPLEX_L2J = 1; -+ public static final byte TYPE_COMPLEX_L2OFF = 0x40; -+ public static final byte TYPE_COMPLEX_L2D = (byte) 0xD1; -+ 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) -+ public static final byte TYPE_MULTILAYER_L2D = (byte) 0xD2; -+ -+ 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; -+ -+ // regions -+ 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; -+ -+ // global geodata -+ private static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); -+ private 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; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (nonexistent) -@@ -1,42 +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 . -- */ --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); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (working copy) -@@ -0,0 +1,53 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public interface IGeoObject -+{ -+ /** -+ * Returns geodata X coordinate of the {@link IGeoObject}. -+ * @return int : Geodata X coordinate. -+ */ -+ int getGeoX(); -+ -+ /** -+ * Returns geodata Y coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Y coordinate. -+ */ -+ int getGeoY(); -+ -+ /** -+ * Returns geodata Z coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Z coordinate. -+ */ -+ int getGeoZ(); -+ -+ /** -+ * Returns height of the {@link IGeoObject}. -+ * @return int : Height. -+ */ -+ int getHeight(); -+ -+ /** -+ * Returns {@link IGeoObject} data. -+ * @return byte[][] : {@link IGeoObject} data. -+ */ -+ byte[][] getObjectGeoData(); -+} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (nonexistent) -@@ -1,47 +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 . -- */ --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(); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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); -- -- // 1 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (nonexistent) -@@ -1,55 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --/** -- * @author HorridoJoho -- */ --public final class NullRegion implements IRegion --{ -- public static final NullRegion INSTANCE = new NullRegion(); -- -- @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 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Region.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (nonexistent) -@@ -1,92 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (nonexistent) -@@ -1,87 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public abstract class AbstractNode --{ -- private T _loc; -- private AbstractNode _parent; -- -- public AbstractNode(T loc) -- { -- _loc = loc; -- } -- -- public void setParent(AbstractNode p) -- { -- _parent = p; -- } -- -- public AbstractNode getParent() -- { -- return _parent; -- } -- -- public T getLoc() -- { -- return _loc; -- } -- -- public void setLoc(T l) -- { -- _loc = l; -- } -- -- @Override -- public int hashCode() -- { -- final int prime = 31; -- int result = 1; -- result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); -- return result; -- } -- -- @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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (nonexistent) -@@ -1,33 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --/** -- * @author -Nemesiss- -- */ --public abstract class AbstractNodeLoc --{ -- public abstract int getX(); -- -- public abstract int getY(); -- -- public abstract int getZ(); -- -- public abstract int getNodeX(); -- -- public abstract int getNodeY(); --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (nonexistent) -@@ -1,67 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public class CellNode extends AbstractNode --{ -- 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (nonexistent) -@@ -1,348 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --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 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) -- { -- _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(); -- } -- -- public final List debugPath() -- { -- final List 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 void getNeighbors() -- { -- if (!_current.getLoc().canGoAll()) -- { -- 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); -- } -- -- // SouthEast -- if ((nodeE != null) && (nodeS != null)) -- { -- if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) -- { -- addNode(x + 1, y + 1, z, true); -- } -- } -- -- // SouthWest -- if ((nodeS != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) -- { -- addNode(x - 1, y + 1, z, true); -- } -- } -- -- // NorthEast -- if ((nodeN != null) && (nodeE != null)) -- { -- if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) -- { -- addNode(x + 1, y - 1, z, true); -- } -- } -- -- // NorthWest -- if ((nodeN != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) -- { -- addNode(x - 1, y - 1, z, true); -- } -- } -- } -- -- private 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(); -- // reinit node if needed -- if (result.getLoc() != null) -- { -- result.getLoc().set(x, y, z); -- } -- else -- { -- result.setLoc(new NodeLoc(x, y, z)); -- } -- } -- -- return result; -- } -- -- private 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 boolean isHighWeight(int x, int y, int z) -- { -- final CellNode result = getNode(x, y, z); -- if (result == null) -- { -- return true; -- } -- -- if (!result.getLoc().canGoAll()) -- { -- return true; -- } -- if (Math.abs(result.getLoc().getZ() - z) > 16) -- { -- return true; -- } -- -- return false; -- } -- -- private 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (working copy) -@@ -0,0 +1,87 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+ -+/** -+ * @author Hasha -+ */ -+public class Node -+{ -+ // node coords and nswe flag -+ private GeoLocation _loc; -+ -+ // node parent (for reverse path construction) -+ private Node _parent; -+ // node child (for moving over nodes during iteration) -+ private Node _child; -+ -+ // node G cost (movement cost = parent movement cost + current movement cost) -+ private double _cost = -1000; -+ -+ public void setLoc(int x, int y, int z) -+ { -+ _loc = new GeoLocation(x, y, z); -+ } -+ -+ public GeoLocation getLoc() -+ { -+ return _loc; -+ } -+ -+ public void setParent(Node parent) -+ { -+ _parent = parent; -+ } -+ -+ public Node getParent() -+ { -+ return _parent; -+ } -+ -+ public void setChild(Node child) -+ { -+ _child = child; -+ } -+ -+ public Node getChild() -+ { -+ return _child; -+ } -+ -+ public void setCost(double cost) -+ { -+ _cost = cost; -+ } -+ -+ public double getCost() -+ { -+ return _cost; -+ } -+ -+ public void free() -+ { -+ // reset node location -+ _loc = null; -+ -+ // reset node parent, child and cost -+ _parent = null; -+ _child = null; -+ _cost = -1000; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (working copy) -@@ -0,0 +1,306 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import java.util.concurrent.locks.ReentrantLock; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+ -+/** -+ * @author DS, Hasha; Credits to Diamond -+ */ -+public class NodeBuffer -+{ -+ private final ReentrantLock _lock = new ReentrantLock(); -+ private final int _size; -+ private final Node[][] _buffer; -+ -+ // center coordinates -+ private int _cx = 0; -+ private int _cy = 0; -+ -+ // target coordinates -+ private int _gtx = 0; -+ private int _gty = 0; -+ private short _gtz = 0; -+ -+ private Node _current = null; -+ -+ /** -+ * Constructor of NodeBuffer. -+ * @param size : one dimension size of buffer -+ */ -+ public NodeBuffer(int size) -+ { -+ // set size -+ _size = size; -+ -+ // initialize buffer -+ _buffer = new Node[size][size]; -+ for (int x = 0; x < size; x++) -+ { -+ for (int y = 0; y < size; y++) -+ { -+ _buffer[x][y] = 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 Node : first node of path -+ */ -+ public Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) -+ { -+ // set coordinates (middle of the line (gox,goy) - (gtx,gty), will be in the center of the buffer) -+ _cx = gox + ((gtx - gox - _size) / 2); -+ _cy = goy + ((gty - goy - _size) / 2); -+ -+ _gtx = gtx; -+ _gty = gty; -+ _gtz = gtz; -+ -+ _current = getNode(gox, goy, goz); -+ _current.setCost(getCostH(gox, goy, goz)); -+ -+ int count = 0; -+ do -+ { -+ // reached target? -+ if ((_current.getLoc().getGeoX() == _gtx) && (_current.getLoc().getGeoY() == _gty) && (Math.abs(_current.getLoc().getZ() - _gtz) < 8)) -+ { -+ return _current; -+ } -+ -+ // expand current node -+ expand(); -+ -+ // move pointer -+ _current = _current.getChild(); -+ } -+ while ((_current != null) && (count++ < Config.MAX_ITERATIONS)); -+ -+ return null; -+ } -+ -+ public boolean isLocked() -+ { -+ return _lock.tryLock(); -+ } -+ -+ public void free() -+ { -+ _current = null; -+ -+ for (Node[] nodes : _buffer) -+ { -+ for (Node node : nodes) -+ { -+ if (node.getLoc() != null) -+ { -+ node.free(); -+ } -+ } -+ } -+ -+ _lock.unlock(); -+ } -+ -+ /** -+ * Check _current Node and add its neighbors to the buffer. -+ */ -+ private final void expand() -+ { -+ // can't move anywhere, don't expand -+ final byte nswe = _current.getLoc().getNSWE(); -+ if (nswe == 0) -+ { -+ return; -+ } -+ -+ // get geo coords of the node to be expanded -+ final int x = _current.getLoc().getGeoX(); -+ final int y = _current.getLoc().getGeoY(); -+ final short z = (short) _current.getLoc().getZ(); -+ -+ // can move north, expand -+ if ((nswe & GeoStructure.CELL_FLAG_N) != 0) -+ { -+ addNode(x, y - 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move south, expand -+ if ((nswe & GeoStructure.CELL_FLAG_S) != 0) -+ { -+ addNode(x, y + 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_W) != 0) -+ { -+ addNode(x - 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_E) != 0) -+ { -+ addNode(x + 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move north-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NW) != 0) -+ { -+ addNode(x - 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move north-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NE) != 0) -+ { -+ addNode(x + 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SW) != 0) -+ { -+ addNode(x - 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SE) != 0) -+ { -+ addNode(x + 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ } -+ -+ /** -+ * Returns node, if it exists in buffer. -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param z : node Z coord -+ * @return Node : node, if exits in buffer -+ */ -+ private final Node getNode(int x, int y, short z) -+ { -+ // check node X out of coordinates -+ final int ix = x - _cx; -+ if ((ix < 0) || (ix >= _size)) -+ { -+ return null; -+ } -+ -+ // check node Y out of coordinates -+ final int iy = y - _cy; -+ if ((iy < 0) || (iy >= _size)) -+ { -+ return null; -+ } -+ -+ // get node -+ final Node result = _buffer[ix][iy]; -+ -+ // check and update -+ if (result.getLoc() == null) -+ { -+ result.setLoc(x, y, z); -+ } -+ -+ // return node -+ return result; -+ } -+ -+ /** -+ * Add node given by coordinates to the buffer. -+ * @param x : geo X coord -+ * @param y : geo Y coord -+ * @param z : geo Z coord -+ * @param weight : weight of movement to new node -+ */ -+ private final void addNode(int x, int y, short z, int weight) -+ { -+ // get node to be expanded -+ final Node node = getNode(x, y, z); -+ if (node == null) -+ { -+ return; -+ } -+ -+ // Z distance between nearby cells is higher than cell size -+ if (node.getLoc().getZ() > (z + (2 * GeoStructure.CELL_HEIGHT))) -+ { -+ return; -+ } -+ -+ // node was already expanded, return -+ if (node.getCost() >= 0) -+ { -+ return; -+ } -+ -+ node.setParent(_current); -+ if (node.getLoc().getNSWE() != (byte) 0xFF) -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + (weight * Config.OBSTACLE_MULTIPLIER)); -+ } -+ else -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + weight); -+ } -+ -+ Node current = _current; -+ int count = 0; -+ while ((current.getChild() != null) && (count < (Config.MAX_ITERATIONS * 4))) -+ { -+ count++; -+ if (current.getChild().getCost() > node.getCost()) -+ { -+ node.setChild(current.getChild()); -+ break; -+ } -+ current = current.getChild(); -+ } -+ -+ if (count >= (Config.MAX_ITERATIONS * 4)) -+ { -+ System.err.println("Pathfinding: too long loop detected, cost:" + node.getCost()); -+ } -+ -+ current.setChild(node); -+ } -+ -+ /** -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param i : node Z coord -+ * @return double : node cost -+ */ -+ private final double getCostH(int x, int y, int i) -+ { -+ final int dX = x - _gtx; -+ final int dY = y - _gty; -+ final int dZ = (i - _gtz) / GeoStructure.CELL_HEIGHT; -+ -+ // return (Math.abs(dX) + Math.abs(dY) + Math.abs(dZ)) * Config.HEURISTIC_WEIGHT; // Manhattan distance -+ return Math.sqrt((dX * dX) + (dY * dY) + (dZ * dZ)) * Config.HEURISTIC_WEIGHT; // Direct distance -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -- --/** -- * @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 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 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; -- // return super.hashCode(); -- } -- -- @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; -- if (_x != other._x) -- { -- return false; -- } -- if (_y != other._y) -- { -- return false; -- } -- if (_goNorth != other._goNorth) -- { -- return false; -- } -- if (_goEast != other._goEast) -- { -- return false; -- } -- if (_goSouth != other._goSouth) -- { -- return false; -- } -- if (_goWest != other._goWest) -- { -- return false; -- } -- if (_geoHeight != other._geoHeight) -- { -- return false; -- } -- return true; -- } --} -Index: java/org/l2jmobius/gameserver/model/actor/Creature.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/Creature.java (revision 8428) -+++ java/org/l2jmobius/gameserver/model/actor/Creature.java (working copy) -@@ -65,8 +65,6 @@ - import org.l2jmobius.gameserver.enums.TeleportWhereType; - import org.l2jmobius.gameserver.enums.UserInfoType; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; - import org.l2jmobius.gameserver.instancemanager.IdManager; - import org.l2jmobius.gameserver.instancemanager.MapRegionManager; - import org.l2jmobius.gameserver.instancemanager.QuestManager; -@@ -2566,7 +2564,7 @@ - - public boolean disregardingGeodata; - public int onGeodataPathIndex; -- public List geoPath; -+ public List geoPath; - public int geoPathAccurateTx; - public int geoPathAccurateTy; - public int geoPathGtx; -@@ -3443,7 +3441,7 @@ - if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) - { - // Path calculation -- overrides previous movement check -- m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); -+ m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); - if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found - { - if (isPlayer() && !_isFlying && !isInWater) -Index: java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (revision 8424) -+++ java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (working copy) -@@ -12746,7 +12746,7 @@ - { - if (Config.CORRECT_PLAYER_Z) - { -- final int nearestZ = GeoEngine.getInstance().getHigherHeight(getX(), getY(), getZ()); -+ final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); - if (getZ() < nearestZ) - { - teleToLocation(new Location(getX(), getY(), nearestZ)); -Index: java/org/l2jmobius/gameserver/util/GeoUtils.java -=================================================================== ---- java/org/l2jmobius/gameserver/util/GeoUtils.java (revision 8409) -+++ java/org/l2jmobius/gameserver/util/GeoUtils.java (working copy) -@@ -19,7 +19,7 @@ - import java.awt.Color; - - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; - import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; - -@@ -26,25 +26,25 @@ - /** - * @author HorridoJoho - */ --public final class GeoUtils -+public class GeoUtils - { - public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); - - final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); - - while (iter.next()) - { -- final int wx = GeoEngine.getInstance().getWorldX(iter.x()); -- final int wy = GeoEngine.getInstance().getWorldY(iter.y()); -+ final int wx = GeoEngine.getWorldX(iter.x()); -+ final int wy = GeoEngine.getWorldY(iter.y()); - - prim.addPoint(Color.RED, wx, wy, z); - } -@@ -53,21 +53,21 @@ - - public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); -- int wy = GeoEngine.getInstance().getWorldY(prevY); -+ int wx = GeoEngine.getWorldX(prevX); -+ int wy = GeoEngine.getWorldY(prevY); - int wz = iter.z(); - prim.addPoint(Color.RED, wx, wy, wz); - -@@ -78,8 +78,8 @@ - - if ((curX != prevX) || (curY != prevY)) - { -- wx = GeoEngine.getInstance().getWorldX(curX); -- wy = GeoEngine.getInstance().getWorldY(curY); -+ wx = GeoEngine.getWorldX(curX); -+ wy = GeoEngine.getWorldY(curY); - wz = iter.z(); - - prim.addPoint(Color.RED, wx, wy, wz); -@@ -93,7 +93,7 @@ - - private static Color getDirectionColor(int x, int y, int z, int nswe) - { -- if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) -+ if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) - { - return Color.GREEN; - } -@@ -109,9 +109,8 @@ - int iPacket = 0; - - ExServerPrimitive exsp = null; -- final GeoEngine ge = GeoEngine.getInstance(); -- final int playerGx = ge.getGeoX(player.getX()); -- final int playerGy = ge.getGeoY(player.getY()); -+ final int playerGx = GeoEngine.getGeoX(player.getX()); -+ final int playerGy = GeoEngine.getGeoY(player.getY()); - for (int dx = -geoRadius; dx <= geoRadius; ++dx) - { - for (int dy = -geoRadius; dy <= geoRadius; ++dy) -@@ -135,12 +134,12 @@ - final int gx = playerGx + dx; - final int gy = playerGy + dy; - -- final int x = ge.getWorldX(gx); -- final int y = ge.getWorldY(gy); -- final int z = ge.getNearestZ(gx, gy, player.getZ()); -+ final int x = GeoEngine.getWorldX(gx); -+ final int y = GeoEngine.getWorldY(gy); -+ final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); - - // north arrow -- Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); -+ Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); - 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); -@@ -147,7 +146,7 @@ - exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z); - - // east arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); - 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); -@@ -154,13 +153,13 @@ - exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z); - - // south arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); - 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, Cell.NSWE_WEST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); - 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); -@@ -188,15 +187,15 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_EAST; -+ return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_EAST; -+ return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; - } - else - { -- return Cell.NSWE_EAST; -+ return GeoStructure.CELL_FLAG_E; // Direction.EAST; - } - } - else if (x < lastX) // west -@@ -203,26 +202,27 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_WEST; -+ return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_WEST; -+ return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; - } - else - { -- return Cell.NSWE_WEST; -+ return GeoStructure.CELL_FLAG_W; // Direction.WEST; - } - } -- else // unchanged x -+ else -+ // unchanged x - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH; -+ return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH; -+ return GeoStructure.CELL_FLAG_N; // Direction.NORTH; - } - else - { -Index: java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java -=================================================================== ---- java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (nonexistent) -+++ java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (working copy) -@@ -0,0 +1,386 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.tools.geodataconverter; -+ -+import java.io.BufferedOutputStream; -+import java.io.File; -+import java.io.FileOutputStream; -+import java.io.RandomAccessFile; -+import java.nio.ByteOrder; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.Scanner; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.commons.util.PropertiesParser; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoDataConverter -+{ -+ private static GeoFormat _format; -+ private static ABlock[][] _blocks; -+ -+ public static void main(String[] args) -+ { -+ // initialize config -+ loadGeoengineConfigs(); -+ -+ // get geodata format -+ String type = ""; -+ try (Scanner scn = new Scanner(System.in)) -+ { -+ while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) -+ { -+ System.out.println("GeoDataConverter: Select source geodata type:"); -+ System.out.println(" J: L2J (e.g. 23_22.l2j)"); -+ System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); -+ System.out.println(" E: Exit"); -+ System.out.print("Choice: "); -+ type = scn.next(); -+ } -+ } -+ if (type.equalsIgnoreCase("E")) -+ { -+ System.exit(0); -+ } -+ -+ _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; -+ -+ // start conversion -+ System.out.println("GeoDataConverter: Converting all " + _format + " files."); -+ -+ // initialize geodata container -+ _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files -+ int converted = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) -+ { -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) -+ { -+ final String input = String.format(_format.getFilename(), rx, ry); -+ final String filepath = Config.GEODATA_PATH; -+ final File f = new File(filepath + input); -+ if (f.exists() && !f.isDirectory()) -+ { -+ // load geodata -+ if (!loadGeoBlocks(input)) -+ { -+ System.out.println("GeoDataConverter: Unable to load " + input + " region file."); -+ continue; -+ } -+ -+ // recalculate nswe -+ if (!recalculateNswe()) -+ { -+ System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); -+ continue; -+ } -+ -+ // save geodata -+ final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); -+ if (!saveGeoBlocks(output)) -+ { -+ System.out.println("GeoDataConverter: Unable to save " + output + " region file."); -+ continue; -+ } -+ -+ converted++; -+ System.out.println("GeoDataConverter: Created " + output + " region file."); -+ } -+ } -+ } -+ System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); -+ } -+ -+ /** -+ * Loads geo blocks from buffer of the region file. -+ * @param filename : The name of the to load. -+ * @return boolean : True when successful. -+ */ -+ private static boolean loadGeoBlocks(String filename) -+ { -+ // region file is load-able, try to load it -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) -+ { -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) -+ if (_format == GeoFormat.L2OFF) -+ { -+ for (int i = 0; i < 18; i++) -+ { -+ buffer.get(); -+ } -+ } -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ if (_format == GeoFormat.L2J) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2J: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2J: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ else -+ { -+ // get block type -+ final short type = buffer.getShort(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ default: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ if (buffer.remaining() > 0) -+ { -+ System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ return false; -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); -+ return false; -+ } -+ } -+ -+ /** -+ * Recalculate diagonal flags for the region file. -+ * @return boolean : True when successful. -+ */ -+ private static boolean recalculateNswe() -+ { -+ try -+ { -+ for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) -+ { -+ for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) -+ { -+ // get block -+ final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // skip flat blocks -+ if (block instanceof BlockFlat) -+ { -+ continue; -+ } -+ -+ // for complex and multilayer blocks go though all layers -+ short height = Short.MAX_VALUE; -+ int index; -+ while ((index = block.getIndexBelow(x, y, height)) != -1) -+ { -+ // get height and nswe -+ height = block.getHeight(index); -+ byte nswe = block.getNswe(index); -+ -+ // update nswe with diagonal flags -+ nswe = updateNsweBelow(x, y, height, nswe); -+ -+ // set nswe of the cell -+ block.setNswe(index, nswe); -+ } -+ } -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ /** -+ * Updates the NSWE flag with diagonal flags. -+ * @param x : Geodata X coordinate. -+ * @param y : Geodata Y coordinate. -+ * @param z : Geodata Z coordinate. -+ * @param nsweValue : NSWE flag to be updated. -+ * @return byte : Updated NSWE flag. -+ */ -+ private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) -+ { -+ byte nswe = nsweValue; -+ -+ // calculate virtual layer height -+ final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); -+ -+ // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) -+ final byte nsweN = getNsweBelow(x, y - 1, height); -+ final byte nsweS = getNsweBelow(x, y + 1, height); -+ final byte nsweW = getNsweBelow(x - 1, y, height); -+ final byte nsweE = getNsweBelow(x + 1, y, height); -+ -+ // north-west -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NW; -+ } -+ -+ // north-east -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NE; -+ } -+ -+ // south-west -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SW; -+ } -+ -+ // south-east -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SE; -+ } -+ -+ return nswe; -+ } -+ -+ private static byte getNsweBelow(int geoX, int geoY, short worldZ) -+ { -+ // out of geo coordinates -+ if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) -+ { -+ return 0; -+ } -+ -+ // out of geo coordinates -+ if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) -+ { -+ return 0; -+ } -+ -+ // get block -+ final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // get index, when valid, return nswe -+ final int index = block.getIndexBelow(geoX, geoY, worldZ); -+ return index == -1 ? 0 : block.getNswe(index); -+ } -+ -+ /** -+ * Save region file to file. -+ * @param filename : The name of file to save. -+ * @return boolean : True when successful. -+ */ -+ private static boolean saveGeoBlocks(String filename) -+ { -+ try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) -+ { -+ // loop over region blocks and save each block -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[ix][iy].saveBlock(bos); -+ } -+ } -+ -+ // flush data to file -+ bos.flush(); -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ private static void loadGeoengineConfigs() -+ { -+ final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); -+ Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); -+ Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); -+ Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); -+ Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); -+ Config.PATHFINDING = geoData.getBoolean("PathFinding", true); -+ Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -+ Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); -+ Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); -+ Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); -+ Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); -+ Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); -+ } -+} -\ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/data/geodata/Readme.txt b/L2J_Mobius_8.0_Homunculus/dist/game/data/geodata/Readme.txt index a480c8c380..2611882e56 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/data/geodata/Readme.txt +++ b/L2J_Mobius_8.0_Homunculus/dist/game/data/geodata/Readme.txt @@ -23,8 +23,8 @@ a - Prerequisites b - Make it work ---------------------------------------------- -To make geodata working: -* unpack your geodata files into "/data/geodata" folder -* open "/config/GeoEngine.ini" with your favorite text editor and then edit following config: - - CoordSynchronize = 2 -* If you do not use any geodata files, the server will automatically change this setting to -1. +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. diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java index 941690319d..1d1149d6b2 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java +++ b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java @@ -78,7 +78,7 @@ public class BoyAndGirl extends AbstractNpcAI startQuestTimer("NPC_SHOUT", 10000 + (getRandom(5) * 1000), npc, null); npc.setRunning(); final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 200, 600); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); return super.onSpawn(npc); } @@ -86,7 +86,7 @@ public class BoyAndGirl extends AbstractNpcAI public void onMoveFinished(Npc npc) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 200, 600); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); super.onMoveFinished(npc); } diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java index 558f2631e6..3bcf3810cb 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java +++ b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java @@ -48,7 +48,7 @@ public class Devno extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java index 7525cc99e8..ce81f5c05c 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java +++ b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java @@ -48,7 +48,7 @@ public class Eleve extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java index 7d9150b156..f4e942c326 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java +++ b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java @@ -46,7 +46,7 @@ public class Handermonkey extends AbstractNpcAI { final int x = npc.getSpawn().getX() + (getRandom(-100, 100)); final int y = npc.getSpawn().getY() + (getRandom(-100, 100)); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x, y, npc.getZ(), npc.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x, y, npc.getZ(), npc.getInstanceWorld()); addMoveToDesire(npc, loc, 0); } else diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java index c993ce8936..7df61da041 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java +++ b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java @@ -48,7 +48,7 @@ public class Karonf extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java index 6e8cbee920..589d376f6b 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java +++ b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java @@ -48,7 +48,7 @@ public class Marsha extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java index c616abccc6..5c09d531c8 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java +++ b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java @@ -48,7 +48,7 @@ public class Morgan extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java index 3652b4ab02..fe20952121 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java +++ b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java @@ -48,7 +48,7 @@ public class Rubentis extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", 10000 + (getRandom(5) * 1000), npc, null); } diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java index beece64521..b0944f763d 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java +++ b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java @@ -63,7 +63,7 @@ public class Vortex extends AbstractNpcAI final int x = (int) (attackers.getX() + (600 * Math.cos(radians))); final int y = (int) (attackers.getY() + (600 * Math.sin(radians))); final int z = attackers.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(attackers.getX(), attackers.getY(), attackers.getZ(), x, y, z, attackers.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(attackers.getX(), attackers.getY(), attackers.getZ(), x, y, z, attackers.getInstanceWorld()); attackers.broadcastPacket(new FlyToLocation(attackers, x, y, z, FlyToLocation.FlyType.THROW_UP, 800, 800, 800)); attackers.setXYZ(loc); attackers.broadcastPacket(new ValidateLocation(attackers)); diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/others/FleeMonsters.java b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/others/FleeMonsters.java index bdbb61d8ec..def15d8afa 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/others/FleeMonsters.java +++ b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/ai/others/FleeMonsters.java @@ -67,7 +67,7 @@ public class FleeMonsters extends AbstractNpcAI final int posX = (int) (npc.getX() + (FLEE_DISTANCE * Math.cos(radians))); final int posY = (int) (npc.getY() + (FLEE_DISTANCE * Math.sin(radians))); final int posZ = npc.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, attacker.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); return super.onAttack(npc, attacker, damage, isSummon); } diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index 524652cbc1..18511c6541 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { @@ -133,8 +133,8 @@ public class AdminGeodata implements IAdminCommandHandler } case "admin_geomap": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y + " (" + ((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + "," + ((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + " to " + ((((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + "," + ((((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + ")"); break; } diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java index f3dffa1227..76572f7f25 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java +++ b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java @@ -57,8 +57,8 @@ public class AdminMissingHtmls implements IAdminCommandHandler { case "admin_geomap_missing_htmls": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final int topLeftX = (x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE; final int topLeftY = (y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE; final int bottomRightX = (((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1; diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index fdb2319138..a0a34f1051 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -19,9 +19,9 @@ package handlers.admincommandhandlers; import java.util.List; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; +import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.util.BuilderUtil; @@ -44,13 +44,13 @@ public class AdminPathNode implements IAdminCommandHandler } if (activeChar.getTarget() != null) { - final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { BuilderUtil.sendSysMessage(activeChar, "No Route!"); return true; } - for (AbstractNodeLoc a : path) + for (Location a : path) { BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/Blink.java b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/Blink.java index 22f2c84eae..22697e8e20 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/Blink.java +++ b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/Blink.java @@ -88,7 +88,7 @@ public class Blink extends AbstractEffect final int y = effected.getY() + y1; final int z = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, destination, _flyType, _flySpeed, _flyDelay, _animationSpeed)); diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/Fear.java b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/Fear.java index 95a5afdf98..88c795e04b 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/Fear.java +++ b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/Fear.java @@ -100,7 +100,7 @@ public class Fear extends AbstractEffect final int posY = (int) (effected.getY() + (FEAR_RANGE * Math.sin(radians))); final int posZ = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); } } diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java index fddd94c60a..55c2e78d8a 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java +++ b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java @@ -57,7 +57,7 @@ public class FlyAway extends AbstractEffect final int y = (int) (effector.getY() - (nRadius * (dy / distance))); final int z = effector.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.broadcastPacket(new FlyToLocation(effected, destination, FlyType.THROW_UP)); effected.setXYZ(destination); diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java index 3c3f9aca62..14809ac499 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java +++ b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java @@ -179,7 +179,7 @@ public class KnockBack extends AbstractEffect final int x = (int) (effected.getX() + (_distance * Math.cos(radians))); final int y = (int) (effected.getY() + (_distance * Math.sin(radians))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, loc, _type, _speed, _delay, _animationSpeed)); diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/PullBack.java b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/PullBack.java index ed6a37eff6..38fe2fe9d0 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/PullBack.java +++ b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/PullBack.java @@ -73,7 +73,7 @@ public class PullBack extends AbstractEffect } // In retail, you get debuff, but you are not even moved if there is obstacle. You are still disabled from using skills and moving though. - if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effector.getInstanceWorld())) + if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effected.getInstanceWorld())) { effected.broadcastPacket(new FlyToLocation(effected, effector, _type, _speed, _delay, _animationSpeed)); effected.setXYZ(effector.getX(), effector.getY(), GeoEngine.getInstance().getHeight(effector.getX(), effector.getY(), effector.getZ()) + 10); diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java index d6f9664141..813e496c47 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java +++ b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java @@ -87,7 +87,7 @@ public class TeleportToSummon extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = summon.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java index d3c263978c..c9dc34ac22 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java +++ b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java @@ -76,7 +76,7 @@ public class TeleportToTarget extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java index f2148adb54..c7ca6f64cb 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java +++ b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java @@ -65,7 +65,7 @@ public class RollingDice implements IItemHandler final int x = player.getX() + x1; final int y = player.getY() + y1; final int z = player.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); Broadcast.toSelfAndKnownPlayers(player, new Dice(player.getObjectId(), itemId, number, destination.getX(), destination.getY(), destination.getZ())); final SystemMessage sm = new SystemMessage(SystemMessageId.C1_HAS_ROLLED_A_S2); diff --git a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/targethandlers/Ground.java b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/targethandlers/Ground.java index acce8f1b2f..1328ff01b5 100644 --- a/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/targethandlers/Ground.java +++ b/L2J_Mobius_8.0_Homunculus/dist/game/data/scripts/handlers/targethandlers/Ground.java @@ -52,7 +52,7 @@ public class Ground implements ITargetTypeHandler return null; } - if (!GeoEngine.getInstance().canSeeTarget(creature, worldPosition)) + if (!GeoEngine.getInstance().canSeeLocation(creature, worldPosition)) { if (sendMessage) { diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/Config.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/Config.java index a11d851ac7..8ca3b9544b 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/Config.java @@ -61,6 +61,7 @@ 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; @@ -967,12 +968,17 @@ public class Config // GeoEngine // -------------------------------------------------- public static Path GEODATA_PATH; + public static GeoType GEODATA_TYPE; public static boolean PATHFINDING; public static String PATHFIND_BUFFERS; - public static float LOW_WEIGHT; - public static float MEDIUM_WEIGHT; - public static float HIGH_WEIGHT; - public static float DIAGONAL_WEIGHT; + public static int MOVE_WEIGHT; + public static int MOVE_WEIGHT_DIAG; + public static int OBSTACLE_WEIGHT; + 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; /** Attribute System */ public static int S_WEAPON_STONE; @@ -2567,12 +2573,17 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); + GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); - MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); - HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); - DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); + MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); + OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); + HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); + MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); + MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); // Load AllowedPlayerRaces config file (if exists) final PropertiesParser AllowedPlayerRaces = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE); diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/commons/util/CommonUtil.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/commons/util/CommonUtil.java index 14ae3a4560..ab7837d827 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/commons/util/CommonUtil.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/commons/util/CommonUtil.java @@ -584,4 +584,15 @@ public class CommonUtil final DecimalFormat formatter = new DecimalFormat(format, new DecimalFormatSymbols(Locale.ENGLISH)); return formatter.format(value); } + + /** + * @param numToTest : The number to test. + * @param min : The minimum limit. + * @param max : The maximum limit. + * @return the number or one of the limit (mininum / maximum). + */ + public static int limit(int numToTest, int min, int max) + { + return (numToTest > max) ? max : ((numToTest < min) ? min : numToTest); + } } diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/commons/util/Point2D.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/commons/util/Point2D.java new file mode 100644 index 0000000000..ebb6272e5e --- /dev/null +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/commons/util/Point2D.java @@ -0,0 +1,180 @@ +/* + * 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 . + */ +package org.l2jmobius.commons.util; + +import java.util.Objects; + +/** + * A datatype used to retain a 2D (x/y) point. It got the capability to be set and cleaned. + */ +public class Point2D +{ + protected volatile int _x; + protected volatile int _y; + + public Point2D(int x, int y) + { + _x = x; + _y = y; + } + + @Override + public Point2D clone() + { + return new Point2D(_x, _y); + } + + @Override + public String toString() + { + return _x + ", " + _y; + } + + @Override + public int hashCode() + { + return Objects.hash(_x, _y); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final Point2D other = (Point2D) obj; + return (_x == other._x) && (_y == other._y); + } + + /** + * @param x : The X coord to test. + * @param y : The Y coord to test. + * @return True if all coordinates equals this {@link Point2D} coordinates. + */ + public boolean equals(int x, int y) + { + return (_x == x) && (_y == y); + } + + public int getX() + { + return _x; + } + + public void setX(int x) + { + _x = x; + } + + public int getY() + { + return _y; + } + + public void setY(int y) + { + _y = y; + } + + public void set(int x, int y) + { + _x = x; + _y = y; + } + + /** + * Refresh the current {@link Point2D} using a reference {@link Point2D} and a distance. The new destination is calculated to go in opposite side of the {@link Point2D} reference.
+ *
+ * This method is perfect to calculate fleeing characters position. + * @param referenceLoc : The Point2D used as position. + * @param distance : The distance to be set between current and new position. + */ + public void setFleeing(Point2D referenceLoc, int distance) + { + final double xDiff = referenceLoc.getX() - _x; + final double yDiff = referenceLoc.getY() - _y; + + final double yxRation = Math.abs(xDiff / yDiff); + + final int y = (int) (distance / (yxRation + 1)); + final int x = (int) (y * yxRation); + + _x += (xDiff < 0 ? x : -x); + _y += (yDiff < 0 ? y : -y); + } + + public void clean() + { + _x = 0; + _y = 0; + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @return The distance between this {@Point2D} and some given coordinates. + */ + public double distance2D(int x, int y) + { + final double dx = (double) _x - x; + final double dy = (double) _y - y; + + return Math.sqrt((dx * dx) + (dy * dy)); + } + + /** + * @param point : The {@link Point2D} to test. + * @return The distance between this {@Point2D} and the {@link Point2D} set as parameter. + */ + public double distance2D(Point2D point) + { + return distance2D(point.getX(), point.getY()); + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of some given coordinates. + */ + public boolean isIn2DRadius(int x, int y, int radius) + { + return distance2D(x, y) < radius; + } + + /** + * @param point : The Point2D to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of the {@link Point2D} set as parameter. + */ + public boolean isIn2DRadius(Point2D point, int radius) + { + return distance2D(point) < radius; + } +} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/ai/AttackableAI.java index 547963e3e8..dc29d4cf6e 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/ai/AttackableAI.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/ai/AttackableAI.java @@ -578,7 +578,7 @@ public class AttackableAI extends CreatureAI } // Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast) - final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); + final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); moveTo(moveLoc.getX(), moveLoc.getY(), moveLoc.getZ()); } } @@ -801,7 +801,7 @@ public class AttackableAI extends CreatureAI final int newZ = npc.getZ() + 30; // Mobius: Verify destination. Prevents wall collision issues and fixes monsters not avoiding obstacles. - moveTo(GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); + moveTo(GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); } return; } diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/data/SpawnTable.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/data/SpawnTable.java index a81201f69e..19f0db5b3e 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/data/SpawnTable.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/data/SpawnTable.java @@ -114,8 +114,8 @@ public class SpawnTable } // XML file for spawn - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); // Write info to XML @@ -192,8 +192,8 @@ public class SpawnTable if (update) { - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = spawn.getNpcSpawnTemplate() != null ? spawn.getNpcSpawnTemplate().getSpawnTemplate().getFile() : new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); final File tempFile = new File(spawnFile.getAbsolutePath().substring(Config.DATAPACK_ROOT.getAbsolutePath().length() + 1).replace('\\', '/') + ".tmp"); try diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/enums/GeoType.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/enums/GeoType.java new file mode 100644 index 0000000000..f77aa5eac4 --- /dev/null +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/enums/GeoType.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.enums; + +public enum GeoType +{ + L2J("%d_%d.l2j"), + L2OFF("%d_%d_conv.dat"); + + private final String _filename; + + private GeoType(String filename) + { + _filename = filename; + } + + public String getFilename() + { + return _filename; + } +} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java new file mode 100644 index 0000000000..e62f50a8df --- /dev/null +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java @@ -0,0 +1,144 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 9d255a5f54..7bef9c770b 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,72 +16,63 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.channels.FileChannel.MapMode; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.logging.Level; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; import java.util.logging.Logger; import org.l2jmobius.Config; +import org.l2jmobius.commons.util.CommonUtil; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; -import org.l2jmobius.gameserver.geoengine.geodata.IRegion; -import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; -import org.l2jmobius.gameserver.geoengine.geodata.Region; +import org.l2jmobius.gameserver.enums.GeoType; +import org.l2jmobius.gameserver.enums.MoveDirectionType; +import org.l2jmobius.gameserver.geoengine.geodata.ABlock; +import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; +import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; +import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; +import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.model.interfaces.ILocational; -import org.l2jmobius.gameserver.util.GeoUtils; -import org.l2jmobius.gameserver.util.LinePointIterator; -import org.l2jmobius.gameserver.util.LinePointIterator3D; -/** - * @author -Nemesiss-, HorridoJoho - */ public class GeoEngine { - private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - - private static final int WORLD_MIN_X = -655360; - private static final int WORLD_MIN_Y = -589824; - private static final int WORLD_MIN_Z = -16384; - - /** 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; - - /** The regions array */ - private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; - private static final int ELEVATED_SEE_OVER_DISTANCE = 2; - private static final int MAX_SEE_OVER_HEIGHT = 48; - protected GeoEngine() + private final ABlock[][] _blocks; + private final BlockNull _nullBlock; + + // Pre-allocated buffers. + private final BufferHolder[] _buffers; + + public GeoEngine() { LOGGER.info("GeoEngine: Initializing..."); - for (int i = 0; i < _regions.length(); i++) - { - _regions.set(i, NullRegion.INSTANCE); - } + // Initialize block container. + _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; + + // Load null block. + _nullBlock = new BlockNull(); + + // Initialize multilayer temporarily buffer. + BlockMultilayer.initialize(); + + // Load geo files according to geoengine config setup. int loaded = 0; try { @@ -92,23 +83,52 @@ public class GeoEngine final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); if (Files.exists(geoFilePath)) { - try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + // Region file is load-able, try to load it. + if (loadGeoBlocks(regionX, regionY)) { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); loaded++; } } + else + { + // Region file is not load-able, load null blocks. + loadNullBlocks(regionX, regionY); + } } } } catch (Exception e) { - LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + LOGGER.warning("GeoEngine: Failed to load geodata! " + e); System.exit(1); } - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + // Release multilayer block temporarily buffer. + BlockMultilayer.release(); + + String[] array = Config.PATHFIND_BUFFERS.split(";"); + _buffers = new BufferHolder[array.length]; + + int count = 0; + for (int i = 0; i < array.length; i++) + { + String buf = array[i]; + String[] args = buf.split("x"); + + try + { + int size = Integer.parseInt(args[1]); + count += size; + _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); + } + catch (Exception e) + { + LOGGER.warning("Could not load buffer setting:" + buf + ". " + e); + } + } + LOGGER.info("Loaded " + count + " node buffers."); + // Avoid wrong configs when no files are loaded. if ((loaded == 0) && Config.PATHFINDING) { @@ -118,616 +138,913 @@ public class GeoEngine } /** - * @param geoX - * @param geoY - * @return the region + * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer and log this situation. + * @param size : pre-calculated minimal required size + * @return NodeBuffer : buffer */ - private IRegion getRegion(int geoX, int geoY) + private NodeBuffer getBuffer(int size) { - final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); - if ((region < 0) || (region >= _regions.length())) + NodeBuffer current = null; + for (BufferHolder holder : _buffers) { - return null; - } - return _regions.get(region); - } - - /** - * @param filePath - * @param regionX - * @param regionY - * @throws IOException - */ - 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()).load().order(ByteOrder.LITTLE_ENDIAN))); - } - } - - /** - * @param regionX - * @param regionY - */ - public void unloadRegion(int regionX, int regionY) - { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); - } - - /** - * @param geoX - * @param geoY - * @return if geodata exist - */ - public boolean hasGeoPos(int geoX, int geoY) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return false; - } - return region.hasGeo(); - } - - /** - * Checks the specified position for available geodata. - * @param x the world x - * @param y the world y - * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise - */ - public boolean hasGeo(int x, int y) - { - return hasGeoPos(getGeoX(x), getGeoY(y)); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe check - */ - public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return true; - } - return region.checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe anti-corner cut - */ - public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) - { - boolean can = true; - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - return can && checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the nearest Z value - */ - public int getNearestZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNearestZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next lower Z value - */ - public int getNextLowerZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextLowerZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next higher Z value - */ - public int getNextHigherZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextHigherZ(geoX, geoY, worldZ); - } - - /** - * Gets the Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHeight(int x, int y, int z) - { - return getNearestZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next lower Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getLowerHeight(int x, int y, int z) - { - return getNextLowerZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next higher Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHigherHeight(int x, int y, int z) - { - return getNextHigherZ(getGeoX(x), getGeoY(y), z); - } - - /** - * @param worldX - * @return the geo X - */ - public int getGeoX(int worldX) - { - return (worldX - WORLD_MIN_X) / 16; - } - - /** - * @param worldY - * @return the geo Y - */ - public int getGeoY(int worldY) - { - return (worldY - WORLD_MIN_Y) / 16; - } - - /** - * @param worldZ - * @return the geo Z - */ - public int getGeoZ(int worldZ) - { - return (worldZ - WORLD_MIN_Z) / 16; - } - - /** - * @param geoX - * @return the world X - */ - public int getWorldX(int geoX) - { - return (geoX * 16) + WORLD_MIN_X + 8; - } - - /** - * @param geoY - * @return the world Y - */ - public int getWorldY(int geoY) - { - return (geoY * 16) + WORLD_MIN_Y + 8; - } - - /** - * @param geoZ - * @return the world Z - */ - public int getWorldZ(int geoZ) - { - return (geoZ * 16) + WORLD_MIN_Z + 8; - } - - /** - * Can see target. Doors as target always return true. Checks doors between. - * @param cha the character - * @param target the target - * @return {@code true} if the character can see the target (LOS), {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, WorldObject target) - { - if (target.isDoor()) - { - // Can always see doors. - return true; - } - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); - } - - /** - * Can see target. Checks doors between. - * @param cha the character - * @param worldPosition the world position - * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) - { - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param world - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @param tworld the target's instanceId - * @return - */ - public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) - { - return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instance - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) - { - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) - { - return false; - } - return canSeeTarget(x, y, z, tx, ty, tz); - } - - /** - * @param prevX - * @param prevY - * @param prevGeoZ - * @param curX - * @param curY - * @param nswe - * @return the LOS Z value - */ - private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) - { - if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) - { - throw new RuntimeException("Multiple directions!"); - } - - if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) - { - return getNearestZ(curX, curY, prevGeoZ); - } - - return getNextHigherZ(curX, curY, prevGeoZ); - } - - /** - * Can see target. Does not check doors between. - * @param xValue the x coordinate - * @param yValue the y coordinate - * @param zValue the z coordinate - * @param txValue the target's x coordinate - * @param tyValue the target's y coordinate - * @param tzValue the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) - { - int x = xValue; - int y = yValue; - int tx = txValue; - int ty = tyValue; - - int geoX = getGeoX(x); - int geoY = getGeoY(y); - int tGeoX = getGeoX(tx); - int tGeoY = getGeoY(ty); - - int z = getNearestZ(geoX, geoY, zValue); - int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - // Fastpath. - if ((geoX == tGeoX) && (geoY == tGeoY)) - { - if (hasGeoPos(tGeoX, tGeoY)) - { - return z == tz; - } - return true; - } - - if (tz > z) - { - int tmp = tx; - tx = x; - x = tmp; - - tmp = ty; - ty = y; - y = tmp; - - tmp = tz; - tz = z; - z = tmp; - - tmp = tGeoX; - tGeoX = geoX; - geoX = tmp; - - tmp = tGeoY; - tGeoY = geoY; - geoY = tmp; - } - - final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); - // First point is guaranteed to be available, skip it, we can always see our own position. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - final int prevZ = pointIter.z(); - int prevGeoZ = prevZ; - int ptIndex = 0; - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - - if ((curX == prevX) && (curY == prevY)) + // Find proper size of buffer. + if (holder._size < size) { continue; } - final int beeCurZ = pointIter.z(); - int curGeoZ = prevGeoZ; - - // Check if the position has geodata. - if (hasGeoPos(curX, curY)) + // Find unlocked NodeBuffer. + for (NodeBuffer buffer : holder._buffer) { - final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); - curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); - int maxHeight; - if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) + if (!buffer.isLocked()) { - maxHeight = z + MAX_SEE_OVER_HEIGHT; - } - else - { - maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; + continue; } - boolean canSeeThrough = false; - if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) + return buffer; + } + + LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); + + // NodeBuffer not found, allocate temporary buffer. + current = new NodeBuffer(holder._size); + current.isLocked(); + } + + return current; + } + + /** + * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + * @return boolean : True, when geodata file was loaded without problem. + */ + private boolean loadGeoBlocks(int regionX, int regionY) + { + final String filename = String.format(Config.GEODATA_TYPE.getFilename(), regionX, regionY); + final String filepath = Config.GEODATA_PATH + "\\" + filename; + + // Standard load. + try (RandomAccessFile raf = new RandomAccessFile(filepath, "r"); + FileChannel fc = raf.getChannel()) + { + // Initialize file buffer. + MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // Load 18B header for L2OFF geodata (1st and 2nd byte...region X and Y). + if (Config.GEODATA_TYPE == GeoType.L2OFF) + { + for (int i = 0; i < 18; i++) { - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) + buffer.get(); + } + } + + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Loop over region blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + if (Config.GEODATA_TYPE == GeoType.L2J) { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) - { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); + // Get block type. + final byte type = buffer.get(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + case GeoStructure.TYPE_MULTILAYER_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + default: + { + throw new IllegalArgumentException("Unknown block type: " + type); + } + } } else { - canSeeThrough = true; + // Get block type. + final short type = buffer.getShort(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + default: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + } } } + } + + // Check data consistency. + if (buffer.remaining() > 0) + { + LOGGER.warning("Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); + } + + // Loading was successful. + return true; + } + catch (Exception e) + { + // An error occured while loading, load null blocks. + LOGGER.warning("Error loading " + filename + " region file. " + e); + + // Replace whole region file with null blocks. + loadNullBlocks(regionX, regionY); + + // Loading was not successful. + return false; + } + } + + /** + * Loads null blocks. Used when no region file is detected or an error occurs during loading. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + */ + private void loadNullBlocks(int regionX, int regionY) + { + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Load all null blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + _blocks[blockX + ix][blockY + iy] = _nullBlock; + } + } + } + + /** + * Converts world X to geodata X. + * @param worldX + * @return int : Geo X + */ + public static int getGeoX(int worldX) + { + return (worldX - World.WORLD_X_MIN) >> 4; + } + + /** + * Converts world Y to geodata Y. + * @param worldY + * @return int : Geo Y + */ + public static int getGeoY(int worldY) + { + return (worldY - World.WORLD_Y_MIN) >> 4; + } + + /** + * Converts geodata X to world X. + * @param geoX + * @return int : World X + */ + public static int getWorldX(int geoX) + { + return (geoX << 4) + World.WORLD_X_MIN + 8; + } + + /** + * Converts geodata Y to world Y. + * @param geoY + * @return int : World Y + */ + public static int getWorldY(int geoY) + { + return (geoY << 4) + World.WORLD_Y_MIN + 8; + } + + /** + * Returns block of geodata on given coordinates. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return {@link ABlock} : Block of geodata. + */ + public ABlock getBlock(int geoX, int geoY) + { + final int x = geoX / GeoStructure.BLOCK_CELLS_X; + if ((x < 0) || (x >= GeoStructure.GEO_BLOCKS_X)) + { + return null; + } + final int y = geoY / GeoStructure.BLOCK_CELLS_Y; + if ((y < 0) || (y >= GeoStructure.GEO_BLOCKS_Y)) + { + return null; + } + return _blocks[x][y]; + } + + /** + * Check if geo coordinates has geo. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return boolean : True, if given geo coordinates have geodata + */ + public boolean hasGeoPos(int geoX, int geoY) + { + final ABlock block = getBlock(geoX, geoY); + return (block != null) && block.hasGeoPos(); + } + + /** + * Returns the height of cell, which is closest to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell geodata Z coordinate, closest to given coordinates. + */ + public short getHeightNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return (short) worldZ; + } + return block.getHeightNearest(geoX, geoY, worldZ); + } + + /** + * Returns the NSWE flag byte of cell, which is closes to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. + */ + public byte getNsweNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return GeoStructure.CELL_FLAG_ALL; + } + return block.getNsweNearest(geoX, geoY, worldZ); + } + + /** + * Check if world coordinates has geo. + * @param worldX : World X + * @param worldY : World Y + * @return boolean : True, if given world coordinates have geodata + */ + public boolean hasGeo(int worldX, int worldY) + { + return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param loc : The location used as reference. + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(Location loc) + { + return getHeightNearest(getGeoX(loc.getX()), getGeoY(loc.getY()), loc.getZ()); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param worldX : world x + * @param worldY : world y + * @param worldZ : world z + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(int worldX, int worldY, int worldZ) + { + return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); + } + + /** + * Check line of sight from {@link WorldObject} to {@link WorldObject}.
+ * @param object : The origin object. + * @param target : The target object. + * @return True, when object can see target. + */ + public boolean canSeeTarget(WorldObject object, WorldObject target) + { + // Can always see doors. + if (target.isDoor()) + { + return true; + } + + if (object.getInstanceWorld() != target.getInstanceWorld()) + { + return false; + } + + if (DoorData.getInstance().checkIfDoorsBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld())) + { + return false; + } + + // Get object's and target's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + double theight = 0; + if (target instanceof Creature) + { + theight += (((Creature) target).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + return canSee(object.getX(), object.getY(), object.getZ(), oheight, target.getX(), target.getY(), target.getZ(), theight) && canSee(target.getX(), target.getY(), target.getZ(), theight, object.getX(), object.getY(), object.getZ(), oheight); + } + + /** + * Check line of sight from {@link WorldObject} to {@link Location}.
+ * Note: The check uses {@link Location}'s real Z coordinate (e.g. point above ground), not its geodata representation. + * @param object : The origin object. + * @param position : The target position. + * @return True, when object can see position. + */ + public boolean canSeeLocation(WorldObject object, Location position) + { + // Get object and location coordinates. + int ox = object.getX(); + int oy = object.getY(); + int oz = object.getZ(); + int tx = position.getX(); + int ty = position.getY(); + int tz = position.getZ(); + + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld())) + { + return false; + } + + // Get object's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + // Perform geodata check. + return canSee(ox, oy, oz, oheight, tx, ty, tz, 0) && canSee(tx, ty, tz, 0, ox, oy, oz, oheight); + } + + /** + * Simple check for origin to target visibility.
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param oheight : The height of origin, used as start point. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param theight : The height of target, used as end point. + * @return True, when origin can see target. + */ + public boolean canSee(int ox, int oy, int oz, double oheight, int tx, int ty, int tz, double theight) + { + // Get origin geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get target geodata coordinates. + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check being on same cell and layer (index). + // Note: Get index must use origin height increased by cell height, the method returns index to height exclusive self. + int index = block.getIndexBelow(gox, goy, oz + GeoStructure.CELL_HEIGHT); + if (index < 0) + { + return false; + } + + if ((gox == gtx) && (goy == gty)) + { + return index == block.getIndexBelow(gtx, gty, tz + GeoStructure.CELL_HEIGHT); + } + + // Get ground and nswe flag. + int groundZ = block.getHeight(index); + int nswe = block.getNswe(index); + + // Get delta coordinates, slope of line (XY, XZ) and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double dz = (tz + theight) - (oz + oheight); + final double m = (double) dy / dx; + final double mz = dz / Math.sqrt((dx * dx) + (dy * dy)); + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); + + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) + { + // Set next cell in X direction. + gridX += mdt.getStepX(); + gox += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); - if (!canSeeThrough) - { - return false; - } + // Set next cell in Y direction. + gridY += mdt.getStepY(); + goy += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevGeoZ = curGeoZ; - ++ptIndex; + // Get block of the next cell. + block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get line of sight height (including Z slope). + double losz = oz + oheight + Config.MAX_OBSTACLE_HEIGHT; + losz += mz * Math.sqrt(((checkX - ox) * (checkX - ox)) + ((checkY - oy) * (checkY - oy))); + + // Check line of sight going though wall (vertical check). + + // Get index of particular layer, based on last iterated cell conditions. + boolean canMove = (nswe & dir) != 0; + if (canMove) + { + // No wall present, get next cell below current cell. + index = block.getIndexBelow(gox, goy, groundZ + GeoStructure.CELL_IGNORE_HEIGHT); + } + else + { + // Wall present, get next cell above current cell. + index = block.getIndexAbove(gox, goy, groundZ - (2 * GeoStructure.CELL_HEIGHT)); + } + + // Next cell's does not exist (no geodata with valid condition), return fail. + if (index < 0) + { + return false; + } + + // Get next cell's layer height. + int z = block.getHeight(index); + + // Perform sine of sight check (next cell is above line of sight line), return fail. + if (z > losz) + { + return false; + } + + // Next cell is accessible, update z and NSWE. + groundZ = z; + nswe = block.getNswe(index); } + // Iteration is completed, no obstacle is found. return true; } /** - * Move check. - * @param x the x coordinate - * @param y the y coordinate - * @param zValue the z coordinate - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tzValue the target's z coordinate - * @param instance the instance - * @return the last Location (x,y,z) where player can walk - just before wall + * Check movement from coordinates to coordinates.
+ * Note: The Z coordinates are supposed to be already validated geodata coordinates. + * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return True, when target coordinates are reachable from origin coordinates. */ - public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(x); - final int geoY = getGeoY(y); - final int z = getNearestZ(geoX, geoY, zValue); - final int tGeoX = getGeoX(tx); - final int tGeoY = getGeoY(ty); - final int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return new Location(x, y, getHeight(x, y, z)); - } - if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) - { - return new Location(x, y, getHeight(x, y, z)); + return false; } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = z; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return false; + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + int goz = getHeightNearest(gox, goy, oz); + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check movement within same cell. + if ((gox == gtx) && (goy == gty)) + { + return goz == getHeight(tx, ty, tz); + } + + // Get nswe flag. + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - // Can't move, return previous location. - return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check point heading into obstacle, if so return current point. + if ((nswe & dir) == 0) + { + return false; + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return false; + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != tz)) - { - // Different floors, return start location. - return new Location(x, y, z); - } - - return new Location(tx, ty, tz); + // When origin Z is target Z, the move is successful. + return goz == getHeight(tx, ty, tz); } /** - * Checks if its possible to move from one location to another. - * @param fromX the X coordinate to start checking from - * @param fromY the Y coordinate to start checking from - * @param fromZvalue the Z coordinate to start checking from - * @param toX the X coordinate to end checking at - * @param toY the Y coordinate to end checking at - * @param toZvalue the Z coordinate to end checking at - * @param instance the instance - * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + * Check movement from origin to target coordinates. Returns last available point in the checked path.
+ * Target X and Y reachable and Z is on same floor: + *
    + *
  • Location of the target with corrected Z value from geodata.
  • + *
+ * Target X and Y reachable but Z is on another floor: + *
    + *
  • Location of the origin with corrected Z value from geodata.
  • + *
+ * Target X and Y not reachable: + *
    + *
  • Last accessible location in destination to target.
  • + *
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return The {@link Location} representing last point of movement (e.g. just before wall). */ - public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + public Location getValidLocation(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(fromX); - final int geoY = getGeoY(fromY); - final int fromZ = getNearestZ(geoX, geoY, fromZvalue); - final int tGeoX = getGeoX(toX); - final int tGeoY = getGeoY(toY); - final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); - - if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) - { - return false; + return new Location(ox, oy, oz); } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = fromZ; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return new Location(ox, oy, oz); + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + final int gtz = getHeightNearest(gtx, gty, tz); + int goz = getHeightNearest(gox, goy, oz); + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - return false; - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check target cell is out of geodata grid (world coordinates). + if ((nx < 0) || (nx >= GeoStructure.GEO_CELLS_X) || (ny < 0) || (ny >= GeoStructure.GEO_CELLS_Y)) + { + return new Location(checkX, checkY, goz); + } + + // Check point heading into obstacle, if so return current (border) point. + if ((nswe & dir) == 0) + { + return new Location(checkX, checkY, goz); + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current (border) point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return new Location(checkX, checkY, goz); + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) - { - // Different floors. - return false; - } - - return true; + // Compare Z coordinates: + // If same, path is okay, return target point and fix its Z geodata coordinate. + // If not same, path is does not exist, return origin point. + return goz == gtz ? new Location(tx, ty, gtz) : new Location(ox, oy, oz); } + /** + * Returns the list of location objects as a result of complete path calculation. + * @param ox : origin x + * @param oy : origin y + * @param oz : origin z + * @param tx : target x + * @param ty : target y + * @param tz : target z + * @param instance + * @return {@code List} : complete path from nodes + */ + public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + { + // Get origin and check existing geo coords. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + if (!hasGeoPos(gox, goy)) + { + return Collections.emptyList(); + } + + int goz = getHeightNearest(gox, goy, oz); + + // Get target and check existing geo coords. + int gtx = getGeoX(tx); + int gty = getGeoY(ty); + if (!hasGeoPos(gtx, gty)) + { + return Collections.emptyList(); + } + + int gtz = getHeightNearest(gtx, gty, tz); + + // Prepare buffer for pathfinding calculations. + NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + if (buffer == null) + { + return Collections.emptyList(); + } + + // Find path. + List path = null; + try + { + path = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + if (path.isEmpty()) + { + return Collections.emptyList(); + } + } + catch (Exception e) + { + return Collections.emptyList(); + } + finally + { + buffer.free(); + } + + // Check path. + if (path.size() < 3) + { + return path; + } + + // Get path list iterator. + ListIterator point = path.listIterator(); + + // Get node A (origin). + int nodeAx = ox; + int nodeAy = oy; + int nodeAz = goz; + + // Get node B. + Location nodeB = point.next(); + + // Iterate thought the path to optimize it. + while (point.hasNext()) + { + // Get node C. + Location nodeC = path.get(point.nextIndex()); + + // Check movement from node A to node C. + if (canMoveToTarget(nodeAx, nodeAy, nodeAz, nodeC.getX(), nodeC.getY(), nodeC.getZ(), instance)) + { + // Can move from node A to node C. + // Remove node B. + point.remove(); + } + else + { + // Can not move from node A to node C. + // Set node A (node B is part of path, update A coordinates). + nodeAx = nodeB.getX(); + nodeAy = nodeB.getY(); + nodeAz = nodeB.getZ(); + } + + // Set node B. + nodeB = point.next(); + } + + return path; + } + + /** + * NodeBuffer container with specified size and count of separate buffers. + */ + private static class BufferHolder + { + final int _size; + final List _buffer; + + public BufferHolder(int size, int count) + { + _size = size; + _buffer = new ArrayList<>(count); + + for (int i = 0; i < count; i++) + { + _buffer.add(new NodeBuffer(size)); + } + } + } + + /** + * Returns the instance of the {@link GeoEngine}. + * @return {@link GeoEngine} : The instance. + */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -737,4 +1054,4 @@ public class GeoEngine { protected static final GeoEngine INSTANCE = new GeoEngine(); } -} +} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java deleted file mode 100644 index cc76b7523a..0000000000 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ /dev/null @@ -1,301 +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 . - */ -package org.l2jmobius.gameserver.geoengine; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; -import org.l2jmobius.gameserver.model.World; -import org.l2jmobius.gameserver.model.instancezone.Instance; - -/** - * @author -Nemesiss- - */ -public class GeoEnginePathfinding -{ - private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); - - private BufferInfo[] _buffers; - - protected GeoEnginePathfinding() - { - try - { - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - - _buffers = 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); - } - - _buffers[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"); - } - } - - public boolean pathNodesExist(short regionoffset) - { - return false; - } - - public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) - { - 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)))); - if (buffer == null) - { - return null; - } - - List path = null; - try - { - final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); - - if (result == null) - { - return null; - } - - path = constructPath(result); - } - catch (Exception e) - { - LOGGER.warning(e.getMessage()); - return null; - } - finally - { - buffer.free(); - } - - // check path - if (path.size() < 3) - { - return path; - } - - int currentX, currentY, currentZ; - ListIterator middlePoint; - - middlePoint = path.listIterator(); - currentX = x; - currentY = y; - currentZ = z; - - while (middlePoint.hasNext()) - { - final AbstractNodeLoc locMiddle = middlePoint.next(); - if (!middlePoint.hasNext()) - { - break; - } - - final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); - if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) - { - middlePoint.remove(); - } - else - { - currentX = locMiddle.getX(); - currentY = locMiddle.getY(); - currentZ = locMiddle.getZ(); - } - } - - return path; - } - - /** - * Convert geodata position to pathnode position - * @param geo_pos - * @return pathnode position - */ - public short getNodePos(int geo_pos) - { - return (short) (geo_pos >> 3); // OK? - } - - /** - * Convert node position to pathnode block position - * @param node_pos - * @return pathnode block position (0...255) - */ - public short getNodeBlock(int node_pos) - { - return (short) (node_pos % 256); - } - - public byte getRegionX(int node_pos) - { - return (byte) ((node_pos >> 8) + World.TILE_X_MIN); - } - - public byte getRegionY(int node_pos) - { - return (byte) ((node_pos >> 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 node_x rx - * @return - */ - public int calculateWorldX(short node_x) - { - return World.MAP_MIN_X + (node_x * 128) + 48; - } - - /** - * Convert pathnode y to World y position - * @param node_y - * @return - */ - public int calculateWorldY(short node_y) - { - return World.MAP_MIN_Y + (node_y * 128) + 48; - } - - private List constructPath(AbstractNode nodeValue) - { - final LinkedList path = new LinkedList<>(); - int previousDirectionX = Integer.MIN_VALUE; - int previousDirectionY = Integer.MIN_VALUE; - int directionX, directionY; - - AbstractNode node = nodeValue; - while (node.getParent() != null) - { - directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); - directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); - - // only add a new route point if moving direction changes - if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) - { - previousDirectionX = directionX; - previousDirectionY = directionY; - - path.addFirst(node.getLoc()); - node.setLoc(null); - } - - node = node.getParent(); - } - - return path; - } - - private CellNodeBuffer alloc(int size) - { - CellNodeBuffer current = null; - for (BufferInfo i : _buffers) - { - if (i.mapSize >= size) - { - for (CellNodeBuffer buf : i.buffer) - { - if (buf.lock()) - { - current = buf; - break; - } - } - if (current != null) - { - break; - } - - // not found, allocate temporary buffer - current = new CellNodeBuffer(i.mapSize); - current.lock(); - if (i.buffer.size() < i.count) - { - i.buffer.add(current); - break; - } - } - } - - return current; - } - - private static final class BufferInfo - { - final int mapSize; - final int count; - ArrayList buffer; - - public BufferInfo(int size, int cnt) - { - mapSize = size; - count = cnt; - buffer = new ArrayList<>(count); - } - } - - public static GeoEnginePathfinding getInstance() - { - return SingletonHolder.INSTANCE; - } - - private static class SingletonHolder - { - protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); - } -} diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java new file mode 100644 index 0000000000..49ec35aa1c --- /dev/null +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java @@ -0,0 +1,85 @@ +/* + * 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 . + */ +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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @param index : Index of the cell. + * @return short : Cell geodata Z coordinate, below given coordinates. + */ + public abstract byte getNswe(int index); +} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java new file mode 100644 index 0000000000..f5997bf287 --- /dev/null +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java @@ -0,0 +1,130 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java new file mode 100644 index 0000000000..9af9d2a28a --- /dev/null +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java @@ -0,0 +1,95 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java new file mode 100644 index 0000000000..31fbf5d477 --- /dev/null +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java new file mode 100644 index 0000000000..f77d21d84b --- /dev/null +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java @@ -0,0 +1,68 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +public class BlockNull extends ABlock +{ + @Override + public boolean hasGeoPos() + { + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java deleted file mode 100644 index 61ea4fcf82..0000000000 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java +++ /dev/null @@ -1,48 +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 . - */ -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() - { - } -} diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java deleted file mode 100644 index 3c8de1a7f7..0000000000 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java +++ /dev/null @@ -1,77 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java deleted file mode 100644 index 1ccdefe3da..0000000000 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java +++ /dev/null @@ -1,56 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java new file mode 100644 index 0000000000..691b164e0c --- /dev/null +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ +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_ALL = 0x0F; + + // 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; +} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java deleted file mode 100644 index 25eafc5a04..0000000000 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java +++ /dev/null @@ -1,42 +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 . - */ -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); -} diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java deleted file mode 100644 index 0d2c765002..0000000000 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java +++ /dev/null @@ -1,47 +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 . - */ -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(); -} diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java deleted file mode 100644 index 52df457360..0000000000 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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); - - // 1 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; - } -} diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java deleted file mode 100644 index 72a5a635c7..0000000000 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java +++ /dev/null @@ -1,55 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author HorridoJoho - */ -public final class NullRegion implements IRegion -{ - public static final NullRegion INSTANCE = new NullRegion(); - - @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 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; - } -} diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java deleted file mode 100644 index fc924cb875..0000000000 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java +++ /dev/null @@ -1,92 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java deleted file mode 100644 index 5087513ed9..0000000000 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java +++ /dev/null @@ -1,87 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public abstract class AbstractNode -{ - private T _loc; - private AbstractNode _parent; - - public AbstractNode(T loc) - { - _loc = loc; - } - - public void setParent(AbstractNode p) - { - _parent = p; - } - - public AbstractNode getParent() - { - return _parent; - } - - public T getLoc() - { - return _loc; - } - - public void setLoc(T l) - { - _loc = l; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); - return result; - } - - @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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java deleted file mode 100644 index 7223b5b2be..0000000000 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java +++ /dev/null @@ -1,33 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -/** - * @author -Nemesiss- - */ -public abstract class AbstractNodeLoc -{ - public abstract int getX(); - - public abstract int getY(); - - public abstract int getZ(); - - public abstract int getNodeX(); - - public abstract int getNodeY(); -} diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java deleted file mode 100644 index 3425234e0e..0000000000 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public class CellNode extends AbstractNode -{ - 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java deleted file mode 100644 index 522610bb7c..0000000000 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java +++ /dev/null @@ -1,348 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -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 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) - { - _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(); - } - - public final List debugPath() - { - final List 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 void getNeighbors() - { - if (!_current.getLoc().canGoAll()) - { - 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); - } - - // SouthEast - if ((nodeE != null) && (nodeS != null)) - { - if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) - { - addNode(x + 1, y + 1, z, true); - } - } - - // SouthWest - if ((nodeS != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) - { - addNode(x - 1, y + 1, z, true); - } - } - - // NorthEast - if ((nodeN != null) && (nodeE != null)) - { - if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) - { - addNode(x + 1, y - 1, z, true); - } - } - - // NorthWest - if ((nodeN != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) - { - addNode(x - 1, y - 1, z, true); - } - } - } - - private 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(); - // reinit node if needed - if (result.getLoc() != null) - { - result.getLoc().set(x, y, z); - } - else - { - result.setLoc(new NodeLoc(x, y, z)); - } - } - - return result; - } - - private 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 boolean isHighWeight(int x, int y, int z) - { - final CellNode result = getNode(x, y, z); - if (result == null) - { - return true; - } - - if (!result.getLoc().canGoAll()) - { - return true; - } - if (Math.abs(result.getLoc().getZ() - z) > 16) - { - return true; - } - - return false; - } - - private 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java new file mode 100644 index 0000000000..fa5ca43c2f --- /dev/null +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -0,0 +1,111 @@ +/* + * 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 . + */ +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 geodata values. + private int _geoX; + private int _geoY; + private byte _nswe; + + // 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; + + _costG = 0; + _costH = 0; + _costF = 0; + + _parent = null; + } + + public void setGeo(int gx, int gy, int gz, byte nswe) + { + super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); + + _geoX = gx; + _geoY = gy; + _nswe = nswe; + } + + 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 int getCostF() + { + return _costF; + } + + public Node getParent() + { + return _parent; + } + + @Override + public int compareTo(Node o) + { + return _costF - o._costF; + } +} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java new file mode 100644 index 0000000000..b464212c96 --- /dev/null +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -0,0 +1,368 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.awt.Color; +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; +import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; + +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 _opened; + // Container holding Nodes already explored. + private final List _closed; + + // Target coordinates. + private int _gtx; + private int _gty; + private int _gtz; + + // Pathfinding statistics. + private long _timeStamp; + private long _lastElapsedTime; + + 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 findPath(int gox, int goy, int goz, int gtx, int gty, int gtz) + { + // Set start timestamp. + _timeStamp = System.currentTimeMillis(); + + // 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)); + _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 constructPath() + { + // Create result. + final LinkedList 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; + } + + /** + * Creates list of Nodes to show debug path. + * @param debug : The debug packet to add debug informations in. + */ + public void debugPath(ExServerPrimitive debug) + { + // Add all opened node as yellow points. + for (Node n : _opened) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); + } + + // Add all opened node as blue points. + for (Node n : _closed) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); + } + } + + 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; + + _lastElapsedTime = System.currentTimeMillis() - _timeStamp; + _lock.unlock(); + } + + public long getElapsedTime() + { + return _lastElapsedTime; + } + + /** + * Expand the current {@link Node} by exploring its neighbors (axially and diagonally). + */ + private void expand() + { + // Movement is blocked, skip. + final byte nswe = _current.getNSWE(); + if (nswe == 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; + + // Can move north, expand. + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + } + + // Can move south, expand. + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + } + + // Can move west, expand. + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move east, expand. + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move north-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move north-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + } + + /** + * 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 weight : The weight of movement to the new node. + * @return The nswe of the added node. Blank, if not added. + */ + private byte addNode(int gx, int gy, int gzValue, int weight) + { + // 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]; + + // Set node geodata coordinates. + node.setGeo(gx, gy, gz, nswe); + + // 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, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + _opened.add(node); + _bufferIndex++; + return nswe; + } + + /** + * Calculate cost H value, calculated using diagonal distance method.
+ * 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. + final int dx = Math.abs(gx - _gtx); + final int dy = Math.abs(gy - _gty); + final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + + // Get diagonal and axial differences to the target. + final int dd = Math.min(dx, dy); + final int da = Math.max(dx, dy) - dd; + + // Calculate the diagonal distance of the node to the target. + return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + } +} \ No newline at end of file diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java deleted file mode 100644 index e3bad04b25..0000000000 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; - -/** - * @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 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 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; - // return super.hashCode(); - } - - @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; - if (_x != other._x) - { - return false; - } - if (_y != other._y) - { - return false; - } - if (_goNorth != other._goNorth) - { - return false; - } - if (_goEast != other._goEast) - { - return false; - } - if (_goSouth != other._goSouth) - { - return false; - } - if (_goWest != other._goWest) - { - return false; - } - if (_geoHeight != other._geoHeight) - { - return false; - } - return true; - } -} diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java index fec7e24f5e..c93649d8fd 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java @@ -94,15 +94,15 @@ public class ZoneManager implements IXmlReader private static final Map SETTINGS = new HashMap<>(); private static final int SHIFT_BY = 15; - private static final int OFFSET_X = Math.abs(World.MAP_MIN_X >> SHIFT_BY); - private static final int OFFSET_Y = Math.abs(World.MAP_MIN_Y >> SHIFT_BY); + private static final int OFFSET_X = Math.abs(World.WORLD_X_MIN >> SHIFT_BY); + private static final int OFFSET_Y = Math.abs(World.WORLD_Y_MIN >> SHIFT_BY); private final Map, ConcurrentHashMap> _classZones = new ConcurrentHashMap<>(); private final Map _spawnTerritories = new ConcurrentHashMap<>(); private final AtomicInteger _lastDynamicId = new AtomicInteger(300000); private List _debugItems; - private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.MAP_MAX_X >> SHIFT_BY) + OFFSET_X + 1][(World.MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y + 1]; + private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.WORLD_X_MAX >> SHIFT_BY) + OFFSET_X + 1][(World.WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y + 1]; /** * Instantiates a new zone manager. diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/Location.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/Location.java index 73689ab1a6..ca3b22a3f0 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/Location.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/Location.java @@ -16,30 +16,30 @@ */ package org.l2jmobius.gameserver.model; +import java.util.Objects; + +import org.l2jmobius.commons.util.Point2D; import org.l2jmobius.gameserver.model.interfaces.ILocational; import org.l2jmobius.gameserver.model.interfaces.IPositionable; /** - * Location data transfer object.
- * Contains coordinates data, heading and instance Id. - * @author Zoey76 + * A datatype used to retain a 3D (x/y/z/heading) point. It got the capability to be set and cleaned. */ -public class Location implements IPositionable +public class Location extends Point2D implements IPositionable { - protected int _x; - protected int _y; - protected int _z; - private int _heading; + protected volatile int _z; + protected volatile int _heading; public Location(int x, int y, int z) { - this(x, y, z, 0); + super(x, y); + _z = z; + _heading = 0; } public Location(int x, int y, int z, int heading) { - _x = x; - _y = y; + super(x, y); _z = z; _heading = heading; } @@ -51,9 +51,8 @@ public class Location implements IPositionable public Location(StatSet set) { - _x = set.getInt("x"); - _y = set.getInt("y"); - _z = set.getInt("z"); + super(set.getInt("x", 0), set.getInt("y", 0)); + _z = set.getInt("z", 0); _heading = set.getInt("heading", 0); } @@ -146,6 +145,25 @@ public class Location implements IPositionable _heading = loc.getHeading(); } + @Override + public void clean() + { + super.clean(); + _z = 0; + } + + @Override + public Location clone() + { + return new Location(_x, _y, _z); + } + + @Override + public int hashCode() + { + return (31 * super.hashCode()) + Objects.hash(_z); + } + @Override public boolean equals(Object obj) { diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/World.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/World.java index 4821081142..c2839ea6b9 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/World.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/World.java @@ -68,19 +68,19 @@ public class World public static final int TILE_Y_MAX = 26; public static final int TILE_ZERO_COORD_X = 20; public static final int TILE_ZERO_COORD_Y = 18; - public static final int MAP_MIN_X = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; - public static final int MAP_MIN_Y = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; + public static final int WORLD_X_MIN = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; + public static final int WORLD_Y_MIN = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; - public static final int MAP_MAX_X = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; - public static final int MAP_MAX_Y = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; + public static final int WORLD_X_MAX = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; + public static final int WORLD_Y_MAX = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; /** Calculated offset used so top left region is 0,0 */ - public static final int OFFSET_X = Math.abs(MAP_MIN_X >> SHIFT_BY); - public static final int OFFSET_Y = Math.abs(MAP_MIN_Y >> SHIFT_BY); + public static final int OFFSET_X = Math.abs(WORLD_X_MIN >> SHIFT_BY); + public static final int OFFSET_Y = Math.abs(WORLD_Y_MIN >> SHIFT_BY); /** Number of regions. */ - private static final int REGIONS_X = (MAP_MAX_X >> SHIFT_BY) + OFFSET_X; - private static final int REGIONS_Y = (MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y; + private static final int REGIONS_X = (WORLD_X_MAX >> SHIFT_BY) + OFFSET_X; + private static final int REGIONS_Y = (WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y; /** Map containing all the players in game. */ private static final Map _allPlayers = new ConcurrentHashMap<>(); diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/WorldObject.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/WorldObject.java index 0e5536bbc0..62313ffea1 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/WorldObject.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/WorldObject.java @@ -188,23 +188,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif synchronized (this) { int spawnX = x; - if (spawnX > World.MAP_MAX_X) + if (spawnX > World.WORLD_X_MAX) { - spawnX = World.MAP_MAX_X - 5000; + spawnX = World.WORLD_X_MAX - 5000; } - if (spawnX < World.MAP_MIN_X) + if (spawnX < World.WORLD_X_MIN) { - spawnX = World.MAP_MIN_X + 5000; + spawnX = World.WORLD_X_MIN + 5000; } int spawnY = y; - if (spawnY > World.MAP_MAX_Y) + if (spawnY > World.WORLD_Y_MAX) { - spawnY = World.MAP_MAX_Y - 5000; + spawnY = World.WORLD_Y_MAX - 5000; } - if (spawnY < World.MAP_MIN_Y) + if (spawnY < World.WORLD_Y_MIN) { - spawnY = World.MAP_MIN_Y + 5000; + spawnY = World.WORLD_Y_MIN + 5000; } // Set the x,y,z position of the WorldObject. If flagged with _isSpawned, setXYZ will automatically update world region, so avoid that. @@ -518,23 +518,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif public void setXYZInvisible(int x, int y, int z) { int correctX = x; - if (correctX > World.MAP_MAX_X) + if (correctX > World.WORLD_X_MAX) { - correctX = World.MAP_MAX_X - 5000; + correctX = World.WORLD_X_MAX - 5000; } - if (correctX < World.MAP_MIN_X) + if (correctX < World.WORLD_X_MIN) { - correctX = World.MAP_MIN_X + 5000; + correctX = World.WORLD_X_MIN + 5000; } int correctY = y; - if (correctY > World.MAP_MAX_Y) + if (correctY > World.WORLD_Y_MAX) { - correctY = World.MAP_MAX_Y - 5000; + correctY = World.WORLD_Y_MAX - 5000; } - if (correctY < World.MAP_MIN_Y) + if (correctY < World.WORLD_Y_MIN) { - correctY = World.MAP_MIN_Y + 5000; + correctY = World.WORLD_Y_MIN + 5000; } setXYZ(correctX, correctY, z); diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/actor/Creature.java index 7b79836e40..359bcb0d64 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -65,8 +65,6 @@ 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.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -2585,7 +2583,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3405,8 +3403,8 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe final int originalX = x; final int originalY = y; final int originalZ = z; - final int gtx = (originalX - World.MAP_MIN_X) >> 4; - final int gty = (originalY - World.MAP_MIN_Y) >> 4; + final int gtx = (originalX - World.WORLD_X_MIN) >> 4; + final int gty = (originalY - World.WORLD_Y_MIN) >> 4; if (isOnGeodataPath()) { try @@ -3429,7 +3427,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe && !(((curZ - z) > 300) && (distance < 300))) // Prohibit correcting destination if character wants to fall. { // location different if destination wasn't reached (or just z coord is different) - final Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(curX, curY, curZ, x, y, z, getInstanceWorld()); + final Location destiny = GeoEngine.getInstance().getValidLocation(curX, curY, curZ, x, y, z, getInstanceWorld()); x = destiny.getX(); y = destiny.getY(); z = destiny.getZ(); @@ -3443,7 +3441,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { if (isPlayer() && !_isFlying && !isInWater) diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/actor/Summon.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/actor/Summon.java index 87d65e23d3..ad18df7166 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/actor/Summon.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/actor/Summon.java @@ -106,7 +106,7 @@ public abstract class Summon extends Playable final int x = owner.getX(); final int y = owner.getY(); final int z = owner.getZ(); - final Location location = GeoEngine.getInstance().canMoveToTargetLoc(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, owner.getInstanceWorld()); + final Location location = GeoEngine.getInstance().getValidLocation(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, getInstanceWorld()); setXYZInvisible(location.getX(), location.getY(), location.getZ()); } diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index ba4c469cbe..4f5acd7e73 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -14275,8 +14275,8 @@ public class PlayerInstance extends Playable public boolean isInTimedHuntingZone(int zoneId, int locX, int locY) { - final int x = ((locX - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((locY - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((locX - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((locY - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; switch (zoneId) { diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java index abbe632775..8943842d96 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java @@ -1569,7 +1569,7 @@ public class ItemInstance extends WorldObject if (dropper != null) { final Instance instance = dropper.getInstanceWorld(); - final Location dropDest = GeoEngine.getInstance().canMoveToTargetLoc(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); + final Location dropDest = GeoEngine.getInstance().getValidLocation(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); x = dropDest.getX(); y = dropDest.getY(); z = dropDest.getZ(); diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java index b2be9fb5e2..0bc496e8cc 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java @@ -1179,7 +1179,7 @@ public class SkillCaster implements Runnable } } - final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().canMoveToTargetLoc(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); + final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().getValidLocation(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); creature.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); creature.broadcastPacket(new FlyToLocation(creature, destination, flyType, 0, 0, 333)); diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java index c62c821f44..30068134e3 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java @@ -114,7 +114,7 @@ public class ValidatePosition implements IClientIncomingPacket { if (player.isFalling(_z)) { - final int nearestZ = GeoEngine.getInstance().getHigherHeight(_x, _y, _z); + final int nearestZ = GeoEngine.getInstance().getHeight(_x, _y, _z); if (player.getZ() < nearestZ) { player.setXYZ(_x, _y, nearestZ); diff --git a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/util/GeoUtils.java index 4600a0fc53..19a979819e 100644 --- a/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_8.0_Homunculus/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -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.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; @@ -30,21 +30,21 @@ public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getInstance().getWorldX(iter.x()); - final int wy = GeoEngine.getInstance().getWorldY(iter.y()); + final int wx = GeoEngine.getWorldX(iter.x()); + final int wy = GeoEngine.getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public final class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); - int wy = GeoEngine.getInstance().getWorldY(prevY); + int wx = GeoEngine.getWorldX(prevX); + int wy = GeoEngine.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.getInstance().getWorldX(curX); - wy = GeoEngine.getInstance().getWorldY(curY); + wx = GeoEngine.getWorldX(curX); + wy = GeoEngine.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().checkNearestNswe(x, y, z, nswe)) + if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0) { return Color.GREEN; } @@ -109,9 +109,8 @@ public final class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final GeoEngine ge = GeoEngine.getInstance(); - final int playerGx = ge.getGeoX(player.getX()); - final int playerGy = ge.getGeoY(player.getY()); + final int playerGx = GeoEngine.getGeoX(player.getX()); + final int playerGy = GeoEngine.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -135,32 +134,32 @@ public final class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = ge.getWorldX(gx); - final int y = ge.getWorldY(gy); - final int z = ge.getNearestZ(gx, gy, player.getZ()); + final int x = GeoEngine.getWorldX(gx); + final int y = GeoEngine.getWorldY(gy); + final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); + Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); 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, Cell.NSWE_EAST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); 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, Cell.NSWE_SOUTH); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); 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, Cell.NSWE_WEST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); 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); @@ -188,41 +187,41 @@ public final class GeoUtils { if (y > lastY) { - return Cell.NSWE_SOUTH_EAST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E; } else if (y < lastY) { - return Cell.NSWE_NORTH_EAST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E; } else { - return Cell.NSWE_EAST; + return GeoStructure.CELL_FLAG_E; } } else if (x < lastX) // west { if (y > lastY) { - return Cell.NSWE_SOUTH_WEST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W; } else if (y < lastY) { - return Cell.NSWE_NORTH_WEST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W; } else { - return Cell.NSWE_WEST; + return GeoStructure.CELL_FLAG_W; } } else // unchanged x { if (y > lastY) { - return Cell.NSWE_SOUTH; + return GeoStructure.CELL_FLAG_S; } else if (y < lastY) { - return Cell.NSWE_NORTH; + return GeoStructure.CELL_FLAG_N; } else { diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/config/GeoEngine.ini b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/config/GeoEngine.ini index 609e8a89a7..e740c678dd 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/config/GeoEngine.ini @@ -6,33 +6,44 @@ # at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ 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 + # ================================================================= -# Path finding +# Pathfinding # ================================================================= # 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 +# an alternative path (e.g. walk around obstacle), default: True +PathFinding = True -# Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 -PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 +# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 -# Weight for nodes without obstacles far from walls -LowWeight = 0.5 +# Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 +MoveWeight = 10 +MoveWeightDiag = 14 -# Weight for nodes near walls -MediumWeight = 2 +# 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 -# Weight for nodes with obstacles -HighWeight = 3 +# 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. -# Default: LowWeight * sqrt(2) -DiagonalWeight = 0.707 +# Maximum number of generated nodes per one path-finding process, default 3500 +MaxIterations = 3500 # ================================================================= -# Other +# Line of Sight # ================================================================= -# Correct player Z after falling through the ground. -CorrectPlayerZ = False +# 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 diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/geodata/L2D_GeoEngine.patch b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/geodata/L2D_GeoEngine.patch deleted file mode 100644 index f8577e3a0e..0000000000 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/geodata/L2D_GeoEngine.patch +++ /dev/null @@ -1,6035 +0,0 @@ -Index: dist/game/GeoDataConverter.bat -=================================================================== ---- dist/game/GeoDataConverter.bat (nonexistent) -+++ dist/game/GeoDataConverter.bat (working copy) -@@ -0,0 +1,6 @@ -+@echo off -+title L2D geodata converter -+ -+java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter -+ -+pause -Index: dist/game/GeoDataConverter.sh -=================================================================== ---- dist/game/GeoDataConverter.sh (nonexistent) -+++ dist/game/GeoDataConverter.sh (working copy) -@@ -0,0 +1,4 @@ -+#! /bin/sh -+ -+java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 -+ -Index: dist/game/config/GeoEngine.ini -=================================================================== ---- dist/game/config/GeoEngine.ini (revision 8409) -+++ dist/game/config/GeoEngine.ini (working copy) -@@ -1,6 +1,10 @@ - # ================================================================= - # Geodata - # ================================================================= -+# Because of real-time performance we are using geodata files only in -+# diagonal L2D format now (using filename e.g. 22_16.l2d). -+# L2D geodata can be obtained by conversion of existing L2J or L2OFF geodata. -+# Launch "GeoDataConverter.bat/sh" and follow instructions to start the conversion. - - # 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/ -@@ -13,6 +17,16 @@ - CoordSynchronize = 2 - - # ================================================================= -+# Path checking -+# ================================================================= -+ -+# 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 finding - # ================================================================= - -@@ -23,19 +37,23 @@ - # Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - --# Weight for nodes without obstacles far from walls --LowWeight = 0.5 -+# Base path weight, when moving from one node to another on axis direction, default: 10 -+BaseWeight = 10 - --# Weight for nodes near walls --MediumWeight = 2 -+# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -+DiagonalWeight = 14 - --# Weight for nodes with obstacles --HighWeight = 3 -+# When movement flags of target node is blocked to any direction, multiply movement weight by this multiplier. -+# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 10 -+ObstacleMultiplier = 10 - --# Weight for diagonal movement. --# Default: LowWeight * sqrt(2) --DiagonalWeight = 0.707 -+# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 20 -+# For proper function must be higher than BaseWeight and/or DiagonalWeight. -+HeuristicWeight = 20 - -+# Maximum number of generated nodes per one path-finding process, default 3500 -+MaxIterations = 3500 -+ - # ================================================================= - # Other - # ================================================================= -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (working copy) -@@ -55,9 +55,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -@@ -73,9 +72,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (working copy) -@@ -18,11 +18,11 @@ - - import java.util.List; - --import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -+import org.l2jmobius.gameserver.geoengine.GeoEngine; - import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -+import org.l2jmobius.gameserver.network.SystemMessageId; - import org.l2jmobius.gameserver.util.BuilderUtil; - - public class AdminPathNode implements IAdminCommandHandler -@@ -37,29 +37,30 @@ - { - if (command.equals("admin_path_find")) - { -- if (!Config.PATHFINDING) -- { -- BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); -- return true; -- } - if (activeChar.getTarget() != null) - { -- final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); -+ final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); - if (path == null) - { -- BuilderUtil.sendSysMessage(activeChar, "No Route!"); -- return true; -+ BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); - } -- for (AbstractNodeLoc a : path) -+ else - { -- BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); -+ for (Location point : path) -+ { -+ BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); -+ } - } - } - else - { -- BuilderUtil.sendSysMessage(activeChar, "No Target!"); -+ activeChar.sendPacket(SystemMessageId.INVALID_TARGET); - } - } -+ else -+ { -+ return false; -+ } - return true; - } - -Index: java/org/l2jmobius/Config.java -=================================================================== ---- java/org/l2jmobius/Config.java (revision 8409) -+++ java/org/l2jmobius/Config.java (working copy) -@@ -32,7 +32,6 @@ - import java.net.UnknownHostException; - import java.nio.charset.StandardCharsets; - import java.nio.file.Files; --import java.nio.file.Path; - import java.nio.file.Paths; - import java.time.Duration; - import java.util.ArrayList; -@@ -966,14 +965,17 @@ - // -------------------------------------------------- - // GeoEngine - // -------------------------------------------------- -- public static Path GEODATA_PATH; -+ public static String GEODATA_PATH; -+ public static int COORD_SYNCHRONIZE; -+ public static int PART_OF_CHARACTER_HEIGHT; -+ public static int MAX_OBSTACLE_HEIGHT; - public static boolean PATHFINDING; - public static String PATHFIND_BUFFERS; -- public static float LOW_WEIGHT; -- public static float MEDIUM_WEIGHT; -- public static float HIGH_WEIGHT; -- public static float DIAGONAL_WEIGHT; -- public static int COORD_SYNCHRONIZE; -+ public static int BASE_WEIGHT; -+ public static int DIAGONAL_WEIGHT; -+ public static int HEURISTIC_WEIGHT; -+ public static int OBSTACLE_MULTIPLIER; -+ public static int MAX_ITERATIONS; - public static boolean CORRECT_PLAYER_Z; - - /** Attribute System */ -@@ -2568,14 +2570,17 @@ - - // Load GeoEngine config file (if exists) - final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); -- GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); -+ GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); -+ COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); -+ MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); - PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -- LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); -- MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); -- HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); -- DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); -- COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); -+ DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); -+ OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); -+ HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); -+ MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); - CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); - - // Load AllowedPlayerRaces config file (if exists) -Index: java/org/l2jmobius/gameserver/geoengine/GeoEngine.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (revision 8435) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (working copy) -@@ -16,100 +16,90 @@ - */ - package org.l2jmobius.gameserver.geoengine; - --import java.io.IOException; -+import java.io.File; - import java.io.RandomAccessFile; - import java.nio.ByteOrder; --import java.nio.channels.FileChannel.MapMode; --import java.nio.file.Files; --import java.nio.file.Path; --import java.util.concurrent.atomic.AtomicReferenceArray; --import java.util.logging.Level; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.List; - import java.util.logging.Logger; - - import org.l2jmobius.Config; - import org.l2jmobius.gameserver.data.xml.DoorData; - import org.l2jmobius.gameserver.data.xml.FenceData; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; --import org.l2jmobius.gameserver.geoengine.geodata.IRegion; --import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; --import org.l2jmobius.gameserver.geoengine.geodata.Region; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.World; - import org.l2jmobius.gameserver.model.WorldObject; -+import org.l2jmobius.gameserver.model.actor.Creature; - import org.l2jmobius.gameserver.model.instancezone.Instance; --import org.l2jmobius.gameserver.model.interfaces.ILocational; --import org.l2jmobius.gameserver.util.GeoUtils; --import org.l2jmobius.gameserver.util.LinePointIterator; --import org.l2jmobius.gameserver.util.LinePointIterator3D; -+import org.l2jmobius.gameserver.util.MathUtil; - - /** -- * @author -Nemesiss-, HorridoJoho -+ * @author Hasha - */ - public class GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); -+ protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - -- private static final int WORLD_MIN_X = -655360; -- private static final int WORLD_MIN_Y = -589824; -- private static final int WORLD_MIN_Z = -16384; -+ private final ABlock[][] _blocks; -+ private final BlockNull _nullBlock; - -- /** 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; -- -- /** The regions array */ -- private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); -- -- private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; -- private static final int ELEVATED_SEE_OVER_DISTANCE = 2; -- private static final int MAX_SEE_OVER_HEIGHT = 48; -- -- protected GeoEngine() -+ /** -+ * GeoEngine constructor. Loads all geodata files of chosen geodata format. -+ */ -+ public GeoEngine() - { - LOGGER.info("GeoEngine: Initializing..."); -- for (int i = 0; i < _regions.length(); i++) -- { -- _regions.set(i, NullRegion.INSTANCE); -- } - -+ // initialize block container -+ _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; -+ -+ // load null block -+ _nullBlock = new BlockNull(); -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files according to geoengine config setup - int loaded = 0; -- try -+ long fileSize = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { -- for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { -- for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) -+ final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); -+ if (f.exists() && !f.isDirectory()) - { -- final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); -- if (Files.exists(geoFilePath)) -+ // region file is load-able, try to load it -+ if (loadGeoBlocks(rx, ry)) - { -- try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) -- { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -- loaded++; -- } -+ loaded++; -+ fileSize += f.length(); - } - } -+ else -+ { -+ // region file is not load-able, load null blocks -+ loadNullBlocks(rx, ry); -+ } - } - } -- catch (Exception e) -+ -+ LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -+ if (loaded > 0) - { -- LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); -- System.exit(1); -+ LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); - } - -- LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -- -- // Avoid wrong configs when no files are loaded. -+ // avoid wrong configs when no files are loaded - if (loaded == 0) - { - if (Config.PATHFINDING) -@@ -123,619 +113,820 @@ - LOGGER.info("GeoEngine: Forcing CoordSynchronize setting to -1."); - } - } -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); - } - - /** -- * @param geoX -- * @param geoY -- * @return the region -+ * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. -+ * @return boolean : True, when geodata file was loaded without problem. - */ -- private IRegion getRegion(int geoX, int geoY) -+ private final boolean loadGeoBlocks(int regionX, int regionY) - { -- final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); -- if ((region < 0) || (region >= _regions.length())) -+ final String filename = String.format(GeoFormat.L2D.getFilename(), regionX, regionY); -+ -+ // standard load -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) - { -- return null; -+ // initialize file buffer -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, GeoFormat.L2D); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ } -+ -+ // check data consistency -+ if (buffer.remaining() > 0) -+ { -+ LOGGER.warning("GeoEngine: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ } -+ -+ // loading was successful -+ return true; - } -- return _regions.get(region); -- } -- -- /** -- * @param filePath -- * @param regionX -- * @param regionY -- * @throws IOException -- */ -- 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")) -+ catch (Exception e) - { -- _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -+ // an error occured while loading, load null blocks -+ LOGGER.warning("GeoEngine: Error while loading " + filename + " region file."); -+ LOGGER.warning(e.getMessage()); -+ -+ // replace whole region file with null blocks -+ loadNullBlocks(regionX, regionY); -+ -+ // loading was not successful -+ return false; - } - } - - /** -- * @param regionX -- * @param regionY -+ * Loads null blocks. Used when no region file is detected or an error occurs during loading. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. - */ -- public void unloadRegion(int regionX, int regionY) -+ private final void loadNullBlocks(int regionX, int regionY) - { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); -- } -- -- /** -- * @param geoX -- * @param geoY -- * @return if geodata exist -- */ -- public boolean hasGeoPos(int geoX, int geoY) -- { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // load all null blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { -- return false; -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[blockX + ix][blockY + iy] = _nullBlock; -+ } - } -- return region.hasGeo(); - } - -+ // GEODATA - GENERAL -+ - /** -- * Checks the specified position for available geodata. -- * @param x the world x -- * @param y the world y -- * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise -+ * Converts world X to geodata X. -+ * @param worldX -+ * @return int : Geo X - */ -- public boolean hasGeo(int x, int y) -+ public static int getGeoX(int worldX) - { -- return hasGeoPos(getGeoX(x), getGeoY(y)); -+ return (MathUtil.limit(worldX, World.MAP_MIN_X, World.MAP_MAX_X) - World.MAP_MIN_X) >> 4; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe check -+ * Converts world Y to geodata Y. -+ * @param worldY -+ * @return int : Geo Y - */ -- public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) -+ public static int getGeoY(int worldY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return true; -- } -- return region.checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(worldY, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.MAP_MIN_Y) >> 4; - } - - /** -+ * Converts geodata X to world X. - * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe anti-corner cut -+ * @return int : World X - */ -- public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) -+ public static int getWorldX(int geoX) - { -- boolean can = true; -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- return can && checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(geoX, 0, GeoStructure.GEO_CELLS_X) << 4) + World.MAP_MIN_X + 8; - } - - /** -- * @param geoX -+ * Converts geodata Y to world Y. - * @param geoY -- * @param worldZ -- * @return the nearest Z value -+ * @return int : World Y - */ -- public int getNearestZ(int geoX, int geoY, int worldZ) -+ public static int getWorldY(int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return worldZ; -- } -- return region.getNearestZ(geoX, geoY, worldZ); -+ return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next lower Z value -+ * Returns block of geodata on given coordinates. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return {@link ABlock} : Block of geodata. - */ -- public int getNextLowerZ(int geoX, int geoY, int worldZ) -+ private final ABlock getBlock(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final int x = geoX / GeoStructure.BLOCK_CELLS_X; -+ final int y = geoY / GeoStructure.BLOCK_CELLS_Y; -+ -+ // if x or y is out of array return null -+ if ((x > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) - { -- return worldZ; -+ return _blocks[x][y]; - } -- return region.getNextLowerZ(geoX, geoY, worldZ); -+ return null; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next higher Z value -+ * Check if geo coordinates has geo. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return boolean : True, if given geo coordinates have geodata - */ -- public int getNextHigherZ(int geoX, int geoY, int worldZ) -+ public boolean hasGeoPos(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final ABlock block = getBlock(geoX, geoY); -+ if (block == null) // null block check - { -- return worldZ; -+ // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) -+ // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); -+ return false; - } -- return region.getNextHigherZ(geoX, geoY, worldZ); -+ return block.hasGeoPos(); - } - - /** -- * Gets the Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the height of cell, which is closest to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, closest to given coordinates. - */ -- public int getHeight(int x, int y, int z) -+ public short getHeightNearest(int geoX, int geoY, int worldZ) - { -- return getNearestZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getHeightNearest(geoX, geoY, worldZ) : (short) worldZ; - } - - /** -- * Gets the next lower Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the NSWE flag byte of cell, which is closes to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. - */ -- public int getLowerHeight(int x, int y, int z) -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) - { -- return getNextLowerZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getNsweNearest(geoX, geoY, worldZ) : (byte) 0xFF; - } - - /** -- * Gets the next higher Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Check if world coordinates has geo. -+ * @param worldX : World X -+ * @param worldY : World Y -+ * @return boolean : True, if given world coordinates have geodata - */ -- public int getHigherHeight(int x, int y, int z) -+ public boolean hasGeo(int worldX, int worldY) - { -- return getNextHigherZ(getGeoX(x), getGeoY(y), z); -+ return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); - } - - /** -- * @param worldX -- * @return the geo X -+ * Returns closest Z coordinate according to geodata. -+ * @param worldX : world x -+ * @param worldY : world y -+ * @param worldZ : world z -+ * @return short : nearest Z coordinates according to geodata - */ -- public int getGeoX(int worldX) -+ public short getHeight(int worldX, int worldY, int worldZ) - { -- return (worldX - WORLD_MIN_X) / 16; -+ return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - -- /** -- * @param worldY -- * @return the geo Y -- */ -- public int getGeoY(int worldY) -- { -- return (worldY - WORLD_MIN_Y) / 16; -- } -+ // PATHFINDING - - /** -- * @param worldZ -- * @return the geo Z -+ * Check line of sight from {@link WorldObject} to {@link WorldObject}. -+ * @param origin : The origin object. -+ * @param target : The target object. -+ * @return {@code boolean} : True if origin can see target - */ -- public int getGeoZ(int worldZ) -+ public boolean canSeeTarget(WorldObject origin, WorldObject target) - { -- return (worldZ - WORLD_MIN_Z) / 16; -- } -- -- /** -- * @param geoX -- * @return the world X -- */ -- public int getWorldX(int geoX) -- { -- return (geoX * 16) + WORLD_MIN_X + 8; -- } -- -- /** -- * @param geoY -- * @return the world Y -- */ -- public int getWorldY(int geoY) -- { -- return (geoY * 16) + WORLD_MIN_Y + 8; -- } -- -- /** -- * @param geoZ -- * @return the world Z -- */ -- public int getWorldZ(int geoZ) -- { -- return (geoZ * 16) + WORLD_MIN_Z + 8; -- } -- -- /** -- * Can see target. Doors as target always return true. Checks doors between. -- * @param cha the character -- * @param target the target -- * @return {@code true} if the character can see the target (LOS), {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, WorldObject target) -- { -- if (target.isDoor()) -+ if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) - { -- // Can always see doors. - return true; - } -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param cha the character -- * @param worldPosition the world position -- * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) -- { -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param world -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @param tworld the target's instanceId -- * @return -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) -- { -- return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instance -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) -- { -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) -+ -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = target.getX(); -+ final int ty = target.getY(); -+ final int tz = target.getZ(); -+ -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } -- return canSeeTarget(x, y, z, tx, ty, tz); -- } -- -- /** -- * @param prevX -- * @param prevY -- * @param prevGeoZ -- * @param curX -- * @param curY -- * @param nswe -- * @return the LOS Z value -- */ -- private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) -- { -- if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { -- throw new RuntimeException("Multiple directions!"); -+ return false; - } - -- if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- return getNearestZ(curX, curY, prevGeoZ); -+ return true; - } - -- return getNextHigherZ(curX, curY, prevGeoZ); -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) -+ { -+ return true; -+ } -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) -+ { -+ return goz == gtz; -+ } -+ -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) -+ { -+ oheight = ((Creature) origin).getCollisionHeight() * 2; -+ } -+ -+ double theight = 0; -+ if (target.isCreature()) -+ { -+ theight = ((Creature) target).getCollisionHeight() * 2; -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); - } - - /** -- * Can see target. Does not check doors between. -- * @param xValue the x coordinate -- * @param yValue the y coordinate -- * @param zValue the z coordinate -- * @param txValue the target's x coordinate -- * @param tyValue the target's y coordinate -- * @param tzValue the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -+ * Check line of sight from {@link WorldObject} to {@link Location}. -+ * @param origin : The origin object. -+ * @param position : The target position. -+ * @return {@code boolean} : True if object can see position - */ -- public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) -+ public boolean canSeeTarget(WorldObject origin, Location position) - { -- int x = xValue; -- int y = yValue; -- int tx = txValue; -- int ty = tyValue; -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = position.getX(); -+ final int ty = position.getY(); -+ final int tz = position.getZ(); - -- int geoX = getGeoX(x); -- int geoY = getGeoY(y); -- int tGeoX = getGeoX(tx); -- int tGeoY = getGeoY(ty); -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) -+ { -+ return false; -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) -+ { -+ return false; -+ } - -- int z = getNearestZ(geoX, geoY, zValue); -- int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- // Fastpath. -- if ((geoX == tGeoX) && (geoY == tGeoY)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- if (hasGeoPos(tGeoX, tGeoY)) -- { -- return z == tz; -- } - return true; - } - -- if (tz > z) -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) - { -- int tmp = tx; -- tx = x; -- x = tmp; -- -- tmp = ty; -- ty = y; -- y = tmp; -- -- tmp = tz; -- tz = z; -- z = tmp; -- -- tmp = tGeoX; -- tGeoX = geoX; -- geoX = tmp; -- -- tmp = tGeoY; -- tGeoY = geoY; -- geoY = tmp; -+ return goz == gtz; - } - -- final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); -- // First point is guaranteed to be available, skip it, we can always see our own position. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- final int prevZ = pointIter.z(); -- int prevGeoZ = prevZ; -- int ptIndex = 0; -- while (pointIter.next()) -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight(); -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); -+ } -+ -+ /** -+ * Simple check for origin to target visibility. -+ * @param goxValue : origin X geodata coordinate -+ * @param goyValue : origin Y geodata coordinate -+ * @param gozValue : origin Z geodata coordinate -+ * @param oheight : origin height (if instance of {@link Character}) -+ * @param gtxValue : target X geodata coordinate -+ * @param gtyValue : target Y geodata coordinate -+ * @param gtzValue : target Z geodata coordinate -+ * @param theight : target height (if instance of {@link Character}) -+ * @param instance -+ * @return {@code boolean} : True, when target can be seen. -+ */ -+ private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) -+ { -+ int goz = gozValue; -+ int gtz = gtzValue; -+ int gox = goxValue; -+ int goy = goyValue; -+ int gtx = gtxValue; -+ int gty = gtyValue; -+ -+ // get line of sight Z coordinates -+ double losoz = goz + ((oheight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ double lostz = gtz + ((theight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ -+ // get X delta and signum -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirox = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; -+ final byte dirtx = sx > 0 ? GeoStructure.CELL_FLAG_W : GeoStructure.CELL_FLAG_E; -+ -+ // get Y delta and signum -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte diroy = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ final byte dirty = sy > 0 ? GeoStructure.CELL_FLAG_N : GeoStructure.CELL_FLAG_S; -+ -+ // get Z delta -+ final int dm = Math.max(dx, dy); -+ final double dz = (lostz - losoz) / dm; -+ -+ // get direction flag for diagonal movement -+ final byte diroxy = getDirXY(dirox, diroy); -+ final byte dirtxy = getDirXY(dirtx, dirty); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte diro; -+ byte dirt; -+ -+ // initialize node values -+ int nox = gox; -+ int noy = goy; -+ int ntx = gtx; -+ int nty = gty; -+ byte nsweo = getNsweNearest(gox, goy, goz); -+ byte nswet = getNsweNearest(gtx, gty, gtz); -+ -+ // loop -+ ABlock block; -+ int index; -+ for (int i = 0; i < ((dm + 1) / 2); i++) -+ { -+ // reset direction flag -+ diro = 0; -+ dirt = 0; - -- if ((curX == prevX) && (curY == prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- continue; -+ // calculate next point XY coordinates -+ d -= dy; -+ d += dx; -+ nox += sx; -+ ntx -= sx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroxy; -+ dirt |= dirtxy; - } -+ else if (e2 > -dy) -+ { -+ // calculate next point X coordinate -+ d -= dy; -+ nox += sx; -+ ntx -= sx; -+ diro |= dirox; -+ dirt |= dirtx; -+ } -+ else if (e2 < dx) -+ { -+ // calculate next point Y coordinate -+ d += dx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroy; -+ dirt |= dirty; -+ } - -- final int beeCurZ = pointIter.z(); -- int curGeoZ = prevGeoZ; -- -- // Check if the position has geodata. -- if (hasGeoPos(curX, curY)) - { -- final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); -- curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); -- int maxHeight; -- if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) -+ // get block of the next cell -+ block = getBlock(nox, noy); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nsweo & diro) == 0) - { -- maxHeight = z + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); - } - else - { -- maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); - } - -- boolean canSeeThrough = false; -- if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) -+ // layer does not exist, return -+ if (index == -1) - { -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else -- { -- canSeeThrough = true; -- } -+ return false; - } - -- if (!canSeeThrough) -+ // get layer and next line of sight Z coordinate -+ goz = block.getHeight(index); -+ losoz += dz; -+ -+ // perform line of sight check, return when fails -+ if ((goz - losoz) > Config.MAX_OBSTACLE_HEIGHT) - { - return false; - } -+ -+ // get layer nswe -+ nsweo = block.getNswe(index); - } -+ { -+ // get block of the next cell -+ block = getBlock(ntx, nty); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nswet & dirt) == 0) -+ { -+ index = block.getIndexAbove(ntx, nty, gtz - GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ else -+ { -+ index = block.getIndexBelow(ntx, nty, gtz + GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ -+ // layer does not exist, return -+ if (index == -1) -+ { -+ return false; -+ } -+ -+ // get layer and next line of sight Z coordinate -+ gtz = block.getHeight(index); -+ lostz -= dz; -+ -+ // perform line of sight check, return when fails -+ if ((gtz - lostz) > Config.MAX_OBSTACLE_HEIGHT) -+ { -+ return false; -+ } -+ -+ // get layer nswe -+ nswet = block.getNswe(index); -+ } - -- prevX = curX; -- prevY = curY; -- prevGeoZ = curGeoZ; -- ++ptIndex; -+ // update coords -+ gox = nox; -+ goy = noy; -+ gtx = ntx; -+ gty = nty; - } - -- return true; -+ // when iteration is completed, compare final Z coordinates -+ return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); - } - - /** -- * Move check. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param zValue the z coordinate -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tzValue the target's z coordinate -- * @param instance the instance -- * @return the last Location (x,y,z) where player can walk - just before wall -+ * Check movement from coordinates to coordinates. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {code boolean} : True if target coordinates are reachable from origin coordinates - */ -- public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) -+ public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- final int geoX = getGeoX(x); -- final int geoY = getGeoY(y); -- final int z = getNearestZ(geoX, geoY, zValue); -- final int tGeoX = getGeoX(tx); -- final int tGeoY = getGeoY(ty); -- final int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } -- if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = z; -+ // perform geodata check -+ final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); -+ return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); -+ } -+ -+ /** -+ * Check movement from origin to target. Returns last available point in the checked path. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {@link Location} : Last point where object can walk (just before wall) -+ */ -+ public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) -+ { -+ return new Location(ox, oy, oz); -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) -+ { -+ return new Location(ox, oy, oz); -+ } - -- while (pointIter.next()) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -- -- if (hasGeoPos(prevX, prevY)) -- { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -- { -- // Can't move, return previous location. -- return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); -- } -- } -- -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return new Location(tx, ty, tz); - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != tz)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- // Different floors, return start location. -- return new Location(x, y, z); -+ return new Location(tx, ty, tz); - } - -- return new Location(tx, ty, tz); -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) -+ { -+ return new Location(tx, ty, tz); -+ } -+ -+ // perform geodata check -+ return checkMove(gox, goy, goz, gtx, gty, gtz, instance); - } - - /** -- * Checks if its possible to move from one location to another. -- * @param fromX the X coordinate to start checking from -- * @param fromY the Y coordinate to start checking from -- * @param fromZvalue the Z coordinate to start checking from -- * @param toX the X coordinate to end checking at -- * @param toY the Y coordinate to end checking at -- * @param toZvalue the Z coordinate to end checking at -- * @param instance the instance -- * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise -+ * With this method you can check if a position is visible or can be reached by beeline movement.
-+ * Target X and Y reachable and Z is on same floor: -+ *
    -+ *
  • Location of the target with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y reachable but Z is on another floor: -+ *
    -+ *
  • Location of the origin with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y not reachable: -+ *
    -+ *
  • Last accessible location in destination to target.
  • -+ *
-+ * @param gox : origin X geodata coordinate -+ * @param goy : origin Y geodata coordinate -+ * @param goz : origin Z geodata coordinate -+ * @param gtx : target X geodata coordinate -+ * @param gty : target Y geodata coordinate -+ * @param gtz : target Z geodata coordinate -+ * @param instance -+ * @return {@link GeoLocation} : The last allowed point of movement. - */ -- public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) -+ protected final GeoLocation checkMove(int gox, int goy, int goz, int gtx, int gty, int gtz, Instance instance) - { -- final int geoX = getGeoX(fromX); -- final int geoY = getGeoY(fromY); -- final int fromZ = getNearestZ(geoX, geoY, fromZvalue); -- final int tGeoX = getGeoX(toX); -- final int tGeoY = getGeoY(toY); -- final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); -- -- if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) -+ if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } -- if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) -+ if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = fromZ; -+ // get X delta, signum and direction flag -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirX = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; - -- while (pointIter.next()) -+ // get Y delta, signum and direction flag -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte dirY = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ -+ // get direction flag for diagonal movement -+ final byte dirXY = getDirXY(dirX, dirY); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte direction; -+ -+ // load pointer coordinates -+ int gpx = gox; -+ int gpy = goy; -+ int gpz = goz; -+ -+ // load next pointer -+ int nx = gpx; -+ int ny = gpy; -+ -+ // loop -+ int count = 0; -+ while (count++ < Config.MAX_ITERATIONS) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -+ direction = 0; - -- if (hasGeoPos(prevX, prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -+ d -= dy; -+ d += dx; -+ nx += sx; -+ ny += sy; -+ direction |= dirXY; -+ } -+ else if (e2 > -dy) -+ { -+ d -= dy; -+ nx += sx; -+ direction |= dirX; -+ } -+ else if (e2 < dx) -+ { -+ d += dx; -+ ny += sy; -+ direction |= dirY; -+ } -+ -+ // obstacle found, return -+ if ((getNsweNearest(gpx, gpy, gpz) & direction) == 0) -+ { -+ return new GeoLocation(gpx, gpy, gpz); -+ } -+ -+ // update pointer coordinates -+ gpx = nx; -+ gpy = ny; -+ gpz = getHeightNearest(nx, ny, gpz); -+ -+ // target coordinates reached -+ if ((gpx == gtx) && (gpy == gty)) -+ { -+ if (gpz == gtz) - { -- return false; -+ // path found, Z coordinates are okay, return target point -+ return new GeoLocation(gtx, gty, gtz); - } -+ -+ // path found, Z coordinates are not okay, return last good point -+ return new GeoLocation(gpx, gpy, gpz); - } -+ } -+ -+ return new GeoLocation(gox, goy, goz); -+ } -+ -+ /** -+ * Returns diagonal NSWE flag format of combined two NSWE flags. -+ * @param dirX : X direction NSWE flag -+ * @param dirY : Y direction NSWE flag -+ * @return byte : NSWE flag of combined direction -+ */ -+ private static byte getDirXY(byte dirX, byte dirY) -+ { -+ // check axis directions -+ if (dirY == GeoStructure.CELL_FLAG_N) -+ { -+ if (dirX == GeoStructure.CELL_FLAG_W) -+ { -+ return GeoStructure.CELL_FLAG_NW; -+ } - -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return GeoStructure.CELL_FLAG_NE; - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) -+ if (dirX == GeoStructure.CELL_FLAG_W) - { -- // Different floors. -- return false; -+ return GeoStructure.CELL_FLAG_SW; - } - -- return true; -+ return GeoStructure.CELL_FLAG_SE; - } - -+ /** -+ * Returns the list of location objects as a result of complete path calculation. -+ * @param ox : origin x -+ * @param oy : origin y -+ * @param oz : origin z -+ * @param tx : target x -+ * @param ty : target y -+ * @param tz : target z -+ * @param instance -+ * @return {@code List} : complete path from nodes -+ */ -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ return null; -+ } -+ -+ /** -+ * Returns the instance of the {@link GeoEngine}. -+ * @return {@link GeoEngine} : The instance. -+ */ - public static GeoEngine getInstance() - { - return SingletonHolder.INSTANCE; -@@ -743,6 +934,6 @@ - - private static class SingletonHolder - { -- protected static final GeoEngine INSTANCE = new GeoEngine(); -+ protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); - } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (working copy) -@@ -20,88 +20,84 @@ - import java.util.LinkedList; - import java.util.List; - import java.util.ListIterator; --import java.util.logging.Level; --import java.util.logging.Logger; - - import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; --import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; --import org.l2jmobius.gameserver.model.World; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -+import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.instancezone.Instance; - - /** -- * @author -Nemesiss- -+ * @author Hasha - */ --public class GeoEnginePathfinding -+final class GeoEnginePathfinding extends GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); -+ // pre-allocated buffers -+ private final BufferHolder[] _buffers; - -- private BufferInfo[] _buffers; -- - protected GeoEnginePathfinding() - { -- try -+ super(); -+ -+ final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ _buffers = new BufferHolder[array.length]; -+ int count = 0; -+ for (int i = 0; i < array.length; i++) - { -- final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ final String buf = array[i]; -+ final String[] args = buf.split("x"); - -- _buffers = new BufferInfo[array.length]; -- -- String buf; -- String[] args; -- for (int i = 0; i < array.length; i++) -+ try - { -- buf = array[i]; -- args = buf.split("x"); -- if (args.length != 2) -- { -- throw new Exception("Invalid buffer definition: " + buf); -- } -- -- _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); -+ final int size = Integer.parseInt(args[1]); -+ count += size; -+ _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); - } -+ catch (Exception e) -+ { -+ LOGGER.warning("GeoEnginePathfinding: Can not load buffer setting: " + buf); -+ } - } -- catch (Exception e) -- { -- LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); -- throw new Error("CellPathFinding: load aborted"); -- } -+ -+ LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); - } - -- public boolean pathNodesExist(short regionoffset) -+ @Override -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- return false; -- } -- -- public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) -- { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -- if (!GeoEngine.getInstance().hasGeo(x, y)) -+ // get origin and check existing geo coords -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { - 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)) -+ -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coords -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { - 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)))); -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // Prepare buffer for pathfinding calculations -+ final NodeBuffer buffer = getBuffer(64 + (2 * Math.max(Math.abs(gox - gtx), Math.abs(goy - gty)))); - if (buffer == null) - { - return null; - } - -- List path = null; -+ // find path -+ List path = null; - try - { -- final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); -- -+ final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); - if (result == null) - { - return null; -@@ -125,33 +121,45 @@ - return path; - } - -- int currentX, currentY, currentZ; -- ListIterator middlePoint; -+ // get path list iterator -+ final ListIterator point = path.listIterator(); - -- middlePoint = path.listIterator(); -- currentX = x; -- currentY = y; -- currentZ = z; -+ // get node A (origin) -+ int nodeAx = gox; -+ int nodeAy = goy; -+ short nodeAz = goz; - -- while (middlePoint.hasNext()) -+ // get node B -+ GeoLocation nodeB = (GeoLocation) point.next(); -+ -+ // iterate thought the path to optimize it -+ int count = 0; -+ while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) - { -- final AbstractNodeLoc locMiddle = middlePoint.next(); -- if (!middlePoint.hasNext()) -- { -- break; -- } -+ // get node C -+ final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - -- final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); -- if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) -+ // check movement from node A to node C -+ final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); -+ if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) - { -- middlePoint.remove(); -+ // can move from node A to node C -+ -+ // remove node B -+ point.remove(); - } - else - { -- currentX = locMiddle.getX(); -- currentY = locMiddle.getY(); -- currentZ = locMiddle.getZ(); -+ // can not move from node A to node C -+ -+ // set node A (node B is part of path, update A coordinates) -+ nodeAx = nodeB.getGeoX(); -+ nodeAy = nodeB.getGeoY(); -+ nodeAz = (short) nodeB.getZ(); - } -+ -+ // set node B -+ nodeB = (GeoLocation) point.next(); - } - - return path; -@@ -158,144 +166,102 @@ - } - - /** -- * Convert geodata position to pathnode position -- * @param geo_pos -- * @return pathnode position -+ * Create list of node locations as result of calculated buffer node tree. -+ * @param node : the entry point -+ * @return List : list of node location - */ -- public short getNodePos(int geo_pos) -+ private static List constructPath(Node node) - { -- return (short) (geo_pos >> 3); // OK? -- } -- -- /** -- * Convert node position to pathnode block position -- * @param node_pos -- * @return pathnode block position (0...255) -- */ -- public short getNodeBlock(int node_pos) -- { -- return (short) (node_pos % 256); -- } -- -- public byte getRegionX(int node_pos) -- { -- return (byte) ((node_pos >> 8) + World.TILE_X_MIN); -- } -- -- public byte getRegionY(int node_pos) -- { -- return (byte) ((node_pos >> 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 node_x rx -- * @return -- */ -- public int calculateWorldX(short node_x) -- { -- return World.MAP_MIN_X + (node_x * 128) + 48; -- } -- -- /** -- * Convert pathnode y to World y position -- * @param node_y -- * @return -- */ -- public int calculateWorldY(short node_y) -- { -- return World.MAP_MIN_Y + (node_y * 128) + 48; -- } -- -- private List constructPath(AbstractNode nodeValue) -- { -- final LinkedList path = new LinkedList<>(); -- int previousDirectionX = Integer.MIN_VALUE; -- int previousDirectionY = Integer.MIN_VALUE; -- int directionX, directionY; -+ // create empty list -+ final LinkedList list = new LinkedList<>(); - -- AbstractNode node = nodeValue; -- while (node.getParent() != null) -+ // set direction X/Y -+ int dx = 0; -+ int dy = 0; -+ -+ // get target parent -+ Node target = node; -+ Node parent = target.getParent(); -+ -+ // while parent exists -+ int count = 0; -+ while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { -- directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); -- directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); -+ // get parent <> target direction X/Y -+ final int nx = parent.getLoc().getGeoX() - target.getLoc().getGeoX(); -+ final int ny = parent.getLoc().getGeoY() - target.getLoc().getGeoY(); - -- // only add a new route point if moving direction changes -- if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) -+ // direction has changed? -+ if ((dx != nx) || (dy != ny)) - { -- previousDirectionX = directionX; -- previousDirectionY = directionY; -+ // add node to the beginning of the list -+ list.addFirst(target.getLoc()); - -- path.addFirst(node.getLoc()); -- node.setLoc(null); -+ // update direction X/Y -+ dx = nx; -+ dy = ny; - } - -- node = node.getParent(); -+ // move to next node, set target and get its parent -+ target = parent; -+ parent = target.getParent(); - } - -- return path; -+ // return list -+ return list; - } - -- private CellNodeBuffer alloc(int size) -+ /** -+ * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer. -+ * @param size : pre-calculated minimal required size -+ * @return NodeBuffer : buffer -+ */ -+ private final NodeBuffer getBuffer(int size) - { -- CellNodeBuffer current = null; -- for (BufferInfo i : _buffers) -+ NodeBuffer current = null; -+ for (BufferHolder holder : _buffers) - { -- if (i.mapSize >= size) -+ // Find proper size of buffer -+ if (holder._size < size) - { -- for (CellNodeBuffer buf : i.buffer) -+ continue; -+ } -+ -+ // Find unlocked NodeBuffer -+ for (NodeBuffer buffer : holder._buffer) -+ { -+ if (!buffer.isLocked()) - { -- if (buf.lock()) -- { -- current = buf; -- break; -- } -+ continue; - } -- if (current != null) -- { -- break; -- } - -- // not found, allocate temporary buffer -- current = new CellNodeBuffer(i.mapSize); -- current.lock(); -- if (i.buffer.size() < i.count) -- { -- i.buffer.add(current); -- break; -- } -+ return buffer; - } -+ -+ // NodeBuffer not found, allocate temporary buffer -+ current = new NodeBuffer(holder._size); -+ current.isLocked(); - } - - return current; - } - -- private static final class BufferInfo -+ /** -+ * NodeBuffer container with specified size and count of separate buffers. -+ */ -+ private static final class BufferHolder - { -- final int mapSize; -- final int count; -- ArrayList buffer; -+ final int _size; -+ List _buffer; - -- public BufferInfo(int size, int cnt) -+ public BufferHolder(int size, int count) - { -- mapSize = size; -- count = cnt; -- buffer = new ArrayList<>(count); -+ _size = size; -+ _buffer = new ArrayList<>(count); -+ for (int i = 0; i < count; i++) -+ { -+ _buffer.add(new NodeBuffer(size)); -+ } - } - } -- -- public static GeoEnginePathfinding getInstance() -- { -- return SingletonHolder.INSTANCE; -- } -- -- private static class SingletonHolder -- { -- protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); -- } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (working copy) -@@ -0,0 +1,197 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @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 height of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getHeightNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first above given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, above given coordinates. -+ */ -+ public abstract short getHeightAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first below given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is closest to given coordinates. -+ * @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 the NSWE flag byte of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getNsweNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first above given coordinates. -+ * @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 getNsweAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first below given coordinates. -+ * @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 getNsweBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is closes layer to given coordinates. -+ * @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. -+ * @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 above given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is first layer below given coordinates. -+ * @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 index to data of the cell, which is first layer below given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexBelowOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeight(int index); -+ -+ /** -+ * Returns the height of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightOriginal(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNswe(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNsweOriginal(int index); -+ -+ /** -+ * Sets the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @param nswe : New NSWE flag byte. -+ */ -+ public abstract void setNswe(int index, byte nswe); -+ -+ /** -+ * Saves the block in L2D format to {@link BufferedOutputStream}. Used only for L2D geodata conversion. -+ * @param stream : The stream. -+ * @throws IOException : Can't save the block to steam. -+ */ -+ public abstract void saveBlock(BufferedOutputStream stream) throws IOException; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (working copy) -@@ -0,0 +1,252 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockComplex(ByteBuffer bb, GeoFormat format) -+ { -+ // initialize buffer -+ _buffer = new byte[GeoStructure.BLOCK_CELLS * 3]; -+ -+ // load data -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // 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); -+ } -+ else -+ { -+ // get nswe -+ final byte nswe = bb.get(); -+ _buffer[i * 3] = nswe; -+ -+ // get height -+ final short height = bb.getShort(); -+ _buffer[(i * 3) + 1] = (byte) (height & 0x00FF); -+ _buffer[(i * 3) + 2] = (byte) (height >> 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height > worldZ ? height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height < worldZ ? height : Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public 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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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 int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_COMPLEX_L2D); -+ -+ // write block data -+ stream.write(_buffer, 0, GeoStructure.BLOCK_CELLS * 3); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (working copy) -@@ -0,0 +1,176 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockFlat extends ABlock -+{ -+ protected final short _height; -+ protected byte _nswe; -+ -+ /** -+ * Creates FlatBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockFlat(ByteBuffer bb, GeoFormat format) -+ { -+ _height = bb.getShort(); -+ _nswe = format != GeoFormat.L2D ? 0x0F : (byte) (0xFF); -+ if (format == GeoFormat.L2OFF) -+ { -+ bb.getShort(); -+ } -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return true; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height > worldZ ? _height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height < worldZ ? _height : Short.MAX_VALUE; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height > worldZ ? _nswe : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height < worldZ ? _nswe : 0; -+ } -+ -+ @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return index -+ return _height < worldZ ? 0 : -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _nswe = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_FLAT_L2D); -+ -+ // write height -+ stream.write((byte) (_height & 0x00FF)); -+ stream.write((byte) (_height >> 8)); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (working copy) -@@ -0,0 +1,465 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+import java.nio.ByteOrder; -+import java.util.Arrays; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockMultilayer extends ABlock -+{ -+ private static final int MAX_LAYERS = Byte.MAX_VALUE; -+ -+ private static ByteBuffer _temp; -+ -+ /** -+ * Initializes the temporarily buffer. -+ */ -+ public static void initialize() -+ { -+ // initialize temporarily buffer and sorting mechanism -+ _temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3); -+ _temp.order(ByteOrder.LITTLE_ENDIAN); -+ } -+ -+ /** -+ * Releases temporarily buffer. -+ */ -+ public static void release() -+ { -+ _temp = null; -+ } -+ -+ protected byte[] _buffer; -+ -+ /** -+ * Implicit constructor for children class. -+ */ -+ protected BlockMultilayer() -+ { -+ _buffer = null; -+ } -+ -+ /** -+ * Creates MultilayerBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockMultilayer(ByteBuffer bb, GeoFormat format) -+ { -+ // 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 = format != GeoFormat.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++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // get data -+ final short data = bb.getShort(); -+ -+ // add nswe and height -+ _temp.put((byte) (data & 0x000F)); -+ _temp.putShort((short) ((short) (data & 0xFFF0) >> 1)); -+ } -+ else -+ { -+ // add nswe -+ _temp.put(bb.get()); -+ -+ // add height -+ _temp.putShort(bb.getShort()); -+ } -+ } -+ } -+ -+ // 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 height -+ if (height > worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, return minimum value -+ return Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 height -+ if (height < worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, return maximum value -+ return Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 nswe -+ if (height > worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 nswe -+ if (height < worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @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 first layer data (first from bottom) -+ byte layers = _buffer[index++]; -+ -+ // loop though all cell layers, find closest layer -+ 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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ // get height -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(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]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ // get nswe -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ // set nswe -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_MULTILAYER_L2D); -+ -+ // for each cell -+ int index = 0; -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ // write layers count -+ final byte layers = _buffer[index++]; -+ stream.write(layers); -+ -+ // write cell data -+ stream.write(_buffer, index, layers * 3); -+ -+ // move index to next cell -+ index += layers * 3; -+ } -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (working copy) -@@ -0,0 +1,150 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockNull extends ABlock -+{ -+ private final byte _nswe; -+ -+ public BlockNull() -+ { -+ _nswe = (byte) 0xFF; -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return false; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweBelow(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) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) -+ { -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (nonexistent) -@@ -1,48 +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 . -- */ --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() -- { -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (nonexistent) -@@ -1,77 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (nonexistent) -@@ -1,56 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (working copy) -@@ -0,0 +1,39 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public enum GeoFormat -+{ -+ L2J("%d_%d.l2j"), -+ L2OFF("%d_%d_conv.dat"), -+ L2D("%d_%d.l2d"); -+ -+ private final String _filename; -+ -+ private GeoFormat(String filename) -+ { -+ _filename = filename; -+ } -+ -+ public String getFilename() -+ { -+ return _filename; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (working copy) -@@ -0,0 +1,67 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.geoengine.GeoEngine; -+import org.l2jmobius.gameserver.model.Location; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoLocation extends Location -+{ -+ private byte _nswe; -+ -+ public GeoLocation(int x, int y, int z) -+ { -+ super(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public void set(int x, int y, short z) -+ { -+ super.setXYZ(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public int getGeoX() -+ { -+ return _x; -+ } -+ -+ public int getGeoY() -+ { -+ return _y; -+ } -+ -+ @Override -+ public int getX() -+ { -+ return GeoEngine.getWorldX(_x); -+ } -+ -+ @Override -+ public int getY() -+ { -+ return GeoEngine.getWorldY(_y); -+ } -+ -+ public byte getNSWE() -+ { -+ return _nswe; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (working copy) -@@ -0,0 +1,70 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoStructure -+{ -+ // cells -+ public static final byte CELL_FLAG_E = 1 << 0; -+ public static final byte CELL_FLAG_W = 1 << 1; -+ public static final byte CELL_FLAG_S = 1 << 2; -+ public static final byte CELL_FLAG_N = 1 << 3; -+ public static final byte CELL_FLAG_SE = 1 << 4; -+ public static final byte CELL_FLAG_SW = 1 << 5; -+ public static final byte CELL_FLAG_NE = 1 << 6; -+ public static final byte CELL_FLAG_NW = (byte) (1 << 7); -+ -+ public static final int CELL_HEIGHT = 8; -+ public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6; -+ -+ // blocks -+ public static final byte TYPE_FLAT_L2J_L2OFF = 0; -+ public static final byte TYPE_FLAT_L2D = (byte) 0xD0; -+ public static final byte TYPE_COMPLEX_L2J = 1; -+ public static final byte TYPE_COMPLEX_L2OFF = 0x40; -+ public static final byte TYPE_COMPLEX_L2D = (byte) 0xD1; -+ 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) -+ public static final byte TYPE_MULTILAYER_L2D = (byte) 0xD2; -+ -+ 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; -+ -+ // regions -+ 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; -+ -+ // global geodata -+ private static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); -+ private 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; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (nonexistent) -@@ -1,42 +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 . -- */ --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); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (working copy) -@@ -0,0 +1,53 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public interface IGeoObject -+{ -+ /** -+ * Returns geodata X coordinate of the {@link IGeoObject}. -+ * @return int : Geodata X coordinate. -+ */ -+ int getGeoX(); -+ -+ /** -+ * Returns geodata Y coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Y coordinate. -+ */ -+ int getGeoY(); -+ -+ /** -+ * Returns geodata Z coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Z coordinate. -+ */ -+ int getGeoZ(); -+ -+ /** -+ * Returns height of the {@link IGeoObject}. -+ * @return int : Height. -+ */ -+ int getHeight(); -+ -+ /** -+ * Returns {@link IGeoObject} data. -+ * @return byte[][] : {@link IGeoObject} data. -+ */ -+ byte[][] getObjectGeoData(); -+} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (nonexistent) -@@ -1,47 +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 . -- */ --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(); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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); -- -- // 1 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (nonexistent) -@@ -1,55 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --/** -- * @author HorridoJoho -- */ --public final class NullRegion implements IRegion --{ -- public static final NullRegion INSTANCE = new NullRegion(); -- -- @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 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Region.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (nonexistent) -@@ -1,92 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (nonexistent) -@@ -1,87 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public abstract class AbstractNode --{ -- private T _loc; -- private AbstractNode _parent; -- -- public AbstractNode(T loc) -- { -- _loc = loc; -- } -- -- public void setParent(AbstractNode p) -- { -- _parent = p; -- } -- -- public AbstractNode getParent() -- { -- return _parent; -- } -- -- public T getLoc() -- { -- return _loc; -- } -- -- public void setLoc(T l) -- { -- _loc = l; -- } -- -- @Override -- public int hashCode() -- { -- final int prime = 31; -- int result = 1; -- result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); -- return result; -- } -- -- @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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (nonexistent) -@@ -1,33 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --/** -- * @author -Nemesiss- -- */ --public abstract class AbstractNodeLoc --{ -- public abstract int getX(); -- -- public abstract int getY(); -- -- public abstract int getZ(); -- -- public abstract int getNodeX(); -- -- public abstract int getNodeY(); --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (nonexistent) -@@ -1,67 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public class CellNode extends AbstractNode --{ -- 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (nonexistent) -@@ -1,348 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --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 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) -- { -- _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(); -- } -- -- public final List debugPath() -- { -- final List 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 void getNeighbors() -- { -- if (!_current.getLoc().canGoAll()) -- { -- 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); -- } -- -- // SouthEast -- if ((nodeE != null) && (nodeS != null)) -- { -- if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) -- { -- addNode(x + 1, y + 1, z, true); -- } -- } -- -- // SouthWest -- if ((nodeS != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) -- { -- addNode(x - 1, y + 1, z, true); -- } -- } -- -- // NorthEast -- if ((nodeN != null) && (nodeE != null)) -- { -- if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) -- { -- addNode(x + 1, y - 1, z, true); -- } -- } -- -- // NorthWest -- if ((nodeN != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) -- { -- addNode(x - 1, y - 1, z, true); -- } -- } -- } -- -- private 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(); -- // reinit node if needed -- if (result.getLoc() != null) -- { -- result.getLoc().set(x, y, z); -- } -- else -- { -- result.setLoc(new NodeLoc(x, y, z)); -- } -- } -- -- return result; -- } -- -- private 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 boolean isHighWeight(int x, int y, int z) -- { -- final CellNode result = getNode(x, y, z); -- if (result == null) -- { -- return true; -- } -- -- if (!result.getLoc().canGoAll()) -- { -- return true; -- } -- if (Math.abs(result.getLoc().getZ() - z) > 16) -- { -- return true; -- } -- -- return false; -- } -- -- private 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (working copy) -@@ -0,0 +1,87 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+ -+/** -+ * @author Hasha -+ */ -+public class Node -+{ -+ // node coords and nswe flag -+ private GeoLocation _loc; -+ -+ // node parent (for reverse path construction) -+ private Node _parent; -+ // node child (for moving over nodes during iteration) -+ private Node _child; -+ -+ // node G cost (movement cost = parent movement cost + current movement cost) -+ private double _cost = -1000; -+ -+ public void setLoc(int x, int y, int z) -+ { -+ _loc = new GeoLocation(x, y, z); -+ } -+ -+ public GeoLocation getLoc() -+ { -+ return _loc; -+ } -+ -+ public void setParent(Node parent) -+ { -+ _parent = parent; -+ } -+ -+ public Node getParent() -+ { -+ return _parent; -+ } -+ -+ public void setChild(Node child) -+ { -+ _child = child; -+ } -+ -+ public Node getChild() -+ { -+ return _child; -+ } -+ -+ public void setCost(double cost) -+ { -+ _cost = cost; -+ } -+ -+ public double getCost() -+ { -+ return _cost; -+ } -+ -+ public void free() -+ { -+ // reset node location -+ _loc = null; -+ -+ // reset node parent, child and cost -+ _parent = null; -+ _child = null; -+ _cost = -1000; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (working copy) -@@ -0,0 +1,306 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import java.util.concurrent.locks.ReentrantLock; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+ -+/** -+ * @author DS, Hasha; Credits to Diamond -+ */ -+public class NodeBuffer -+{ -+ private final ReentrantLock _lock = new ReentrantLock(); -+ private final int _size; -+ private final Node[][] _buffer; -+ -+ // center coordinates -+ private int _cx = 0; -+ private int _cy = 0; -+ -+ // target coordinates -+ private int _gtx = 0; -+ private int _gty = 0; -+ private short _gtz = 0; -+ -+ private Node _current = null; -+ -+ /** -+ * Constructor of NodeBuffer. -+ * @param size : one dimension size of buffer -+ */ -+ public NodeBuffer(int size) -+ { -+ // set size -+ _size = size; -+ -+ // initialize buffer -+ _buffer = new Node[size][size]; -+ for (int x = 0; x < size; x++) -+ { -+ for (int y = 0; y < size; y++) -+ { -+ _buffer[x][y] = 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 Node : first node of path -+ */ -+ public Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) -+ { -+ // set coordinates (middle of the line (gox,goy) - (gtx,gty), will be in the center of the buffer) -+ _cx = gox + ((gtx - gox - _size) / 2); -+ _cy = goy + ((gty - goy - _size) / 2); -+ -+ _gtx = gtx; -+ _gty = gty; -+ _gtz = gtz; -+ -+ _current = getNode(gox, goy, goz); -+ _current.setCost(getCostH(gox, goy, goz)); -+ -+ int count = 0; -+ do -+ { -+ // reached target? -+ if ((_current.getLoc().getGeoX() == _gtx) && (_current.getLoc().getGeoY() == _gty) && (Math.abs(_current.getLoc().getZ() - _gtz) < 8)) -+ { -+ return _current; -+ } -+ -+ // expand current node -+ expand(); -+ -+ // move pointer -+ _current = _current.getChild(); -+ } -+ while ((_current != null) && (count++ < Config.MAX_ITERATIONS)); -+ -+ return null; -+ } -+ -+ public boolean isLocked() -+ { -+ return _lock.tryLock(); -+ } -+ -+ public void free() -+ { -+ _current = null; -+ -+ for (Node[] nodes : _buffer) -+ { -+ for (Node node : nodes) -+ { -+ if (node.getLoc() != null) -+ { -+ node.free(); -+ } -+ } -+ } -+ -+ _lock.unlock(); -+ } -+ -+ /** -+ * Check _current Node and add its neighbors to the buffer. -+ */ -+ private final void expand() -+ { -+ // can't move anywhere, don't expand -+ final byte nswe = _current.getLoc().getNSWE(); -+ if (nswe == 0) -+ { -+ return; -+ } -+ -+ // get geo coords of the node to be expanded -+ final int x = _current.getLoc().getGeoX(); -+ final int y = _current.getLoc().getGeoY(); -+ final short z = (short) _current.getLoc().getZ(); -+ -+ // can move north, expand -+ if ((nswe & GeoStructure.CELL_FLAG_N) != 0) -+ { -+ addNode(x, y - 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move south, expand -+ if ((nswe & GeoStructure.CELL_FLAG_S) != 0) -+ { -+ addNode(x, y + 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_W) != 0) -+ { -+ addNode(x - 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_E) != 0) -+ { -+ addNode(x + 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move north-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NW) != 0) -+ { -+ addNode(x - 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move north-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NE) != 0) -+ { -+ addNode(x + 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SW) != 0) -+ { -+ addNode(x - 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SE) != 0) -+ { -+ addNode(x + 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ } -+ -+ /** -+ * Returns node, if it exists in buffer. -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param z : node Z coord -+ * @return Node : node, if exits in buffer -+ */ -+ private final Node getNode(int x, int y, short z) -+ { -+ // check node X out of coordinates -+ final int ix = x - _cx; -+ if ((ix < 0) || (ix >= _size)) -+ { -+ return null; -+ } -+ -+ // check node Y out of coordinates -+ final int iy = y - _cy; -+ if ((iy < 0) || (iy >= _size)) -+ { -+ return null; -+ } -+ -+ // get node -+ final Node result = _buffer[ix][iy]; -+ -+ // check and update -+ if (result.getLoc() == null) -+ { -+ result.setLoc(x, y, z); -+ } -+ -+ // return node -+ return result; -+ } -+ -+ /** -+ * Add node given by coordinates to the buffer. -+ * @param x : geo X coord -+ * @param y : geo Y coord -+ * @param z : geo Z coord -+ * @param weight : weight of movement to new node -+ */ -+ private final void addNode(int x, int y, short z, int weight) -+ { -+ // get node to be expanded -+ final Node node = getNode(x, y, z); -+ if (node == null) -+ { -+ return; -+ } -+ -+ // Z distance between nearby cells is higher than cell size -+ if (node.getLoc().getZ() > (z + (2 * GeoStructure.CELL_HEIGHT))) -+ { -+ return; -+ } -+ -+ // node was already expanded, return -+ if (node.getCost() >= 0) -+ { -+ return; -+ } -+ -+ node.setParent(_current); -+ if (node.getLoc().getNSWE() != (byte) 0xFF) -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + (weight * Config.OBSTACLE_MULTIPLIER)); -+ } -+ else -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + weight); -+ } -+ -+ Node current = _current; -+ int count = 0; -+ while ((current.getChild() != null) && (count < (Config.MAX_ITERATIONS * 4))) -+ { -+ count++; -+ if (current.getChild().getCost() > node.getCost()) -+ { -+ node.setChild(current.getChild()); -+ break; -+ } -+ current = current.getChild(); -+ } -+ -+ if (count >= (Config.MAX_ITERATIONS * 4)) -+ { -+ System.err.println("Pathfinding: too long loop detected, cost:" + node.getCost()); -+ } -+ -+ current.setChild(node); -+ } -+ -+ /** -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param i : node Z coord -+ * @return double : node cost -+ */ -+ private final double getCostH(int x, int y, int i) -+ { -+ final int dX = x - _gtx; -+ final int dY = y - _gty; -+ final int dZ = (i - _gtz) / GeoStructure.CELL_HEIGHT; -+ -+ // return (Math.abs(dX) + Math.abs(dY) + Math.abs(dZ)) * Config.HEURISTIC_WEIGHT; // Manhattan distance -+ return Math.sqrt((dX * dX) + (dY * dY) + (dZ * dZ)) * Config.HEURISTIC_WEIGHT; // Direct distance -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -- --/** -- * @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 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 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; -- // return super.hashCode(); -- } -- -- @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; -- if (_x != other._x) -- { -- return false; -- } -- if (_y != other._y) -- { -- return false; -- } -- if (_goNorth != other._goNorth) -- { -- return false; -- } -- if (_goEast != other._goEast) -- { -- return false; -- } -- if (_goSouth != other._goSouth) -- { -- return false; -- } -- if (_goWest != other._goWest) -- { -- return false; -- } -- if (_geoHeight != other._geoHeight) -- { -- return false; -- } -- return true; -- } --} -Index: java/org/l2jmobius/gameserver/model/actor/Creature.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/Creature.java (revision 8428) -+++ java/org/l2jmobius/gameserver/model/actor/Creature.java (working copy) -@@ -65,8 +65,6 @@ - import org.l2jmobius.gameserver.enums.TeleportWhereType; - import org.l2jmobius.gameserver.enums.UserInfoType; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; - import org.l2jmobius.gameserver.instancemanager.IdManager; - import org.l2jmobius.gameserver.instancemanager.MapRegionManager; - import org.l2jmobius.gameserver.instancemanager.QuestManager; -@@ -2566,7 +2564,7 @@ - - public boolean disregardingGeodata; - public int onGeodataPathIndex; -- public List geoPath; -+ public List geoPath; - public int geoPathAccurateTx; - public int geoPathAccurateTy; - public int geoPathGtx; -@@ -3443,7 +3441,7 @@ - if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) - { - // Path calculation -- overrides previous movement check -- m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); -+ m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); - if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found - { - if (isPlayer() && !_isFlying && !isInWater) -Index: java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (revision 8424) -+++ java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (working copy) -@@ -12746,7 +12746,7 @@ - { - if (Config.CORRECT_PLAYER_Z) - { -- final int nearestZ = GeoEngine.getInstance().getHigherHeight(getX(), getY(), getZ()); -+ final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); - if (getZ() < nearestZ) - { - teleToLocation(new Location(getX(), getY(), nearestZ)); -Index: java/org/l2jmobius/gameserver/util/GeoUtils.java -=================================================================== ---- java/org/l2jmobius/gameserver/util/GeoUtils.java (revision 8409) -+++ java/org/l2jmobius/gameserver/util/GeoUtils.java (working copy) -@@ -19,7 +19,7 @@ - import java.awt.Color; - - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; - import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; - -@@ -26,25 +26,25 @@ - /** - * @author HorridoJoho - */ --public final class GeoUtils -+public class GeoUtils - { - public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); - - final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); - - while (iter.next()) - { -- final int wx = GeoEngine.getInstance().getWorldX(iter.x()); -- final int wy = GeoEngine.getInstance().getWorldY(iter.y()); -+ final int wx = GeoEngine.getWorldX(iter.x()); -+ final int wy = GeoEngine.getWorldY(iter.y()); - - prim.addPoint(Color.RED, wx, wy, z); - } -@@ -53,21 +53,21 @@ - - public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); -- int wy = GeoEngine.getInstance().getWorldY(prevY); -+ int wx = GeoEngine.getWorldX(prevX); -+ int wy = GeoEngine.getWorldY(prevY); - int wz = iter.z(); - prim.addPoint(Color.RED, wx, wy, wz); - -@@ -78,8 +78,8 @@ - - if ((curX != prevX) || (curY != prevY)) - { -- wx = GeoEngine.getInstance().getWorldX(curX); -- wy = GeoEngine.getInstance().getWorldY(curY); -+ wx = GeoEngine.getWorldX(curX); -+ wy = GeoEngine.getWorldY(curY); - wz = iter.z(); - - prim.addPoint(Color.RED, wx, wy, wz); -@@ -93,7 +93,7 @@ - - private static Color getDirectionColor(int x, int y, int z, int nswe) - { -- if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) -+ if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) - { - return Color.GREEN; - } -@@ -109,9 +109,8 @@ - int iPacket = 0; - - ExServerPrimitive exsp = null; -- final GeoEngine ge = GeoEngine.getInstance(); -- final int playerGx = ge.getGeoX(player.getX()); -- final int playerGy = ge.getGeoY(player.getY()); -+ final int playerGx = GeoEngine.getGeoX(player.getX()); -+ final int playerGy = GeoEngine.getGeoY(player.getY()); - for (int dx = -geoRadius; dx <= geoRadius; ++dx) - { - for (int dy = -geoRadius; dy <= geoRadius; ++dy) -@@ -135,12 +134,12 @@ - final int gx = playerGx + dx; - final int gy = playerGy + dy; - -- final int x = ge.getWorldX(gx); -- final int y = ge.getWorldY(gy); -- final int z = ge.getNearestZ(gx, gy, player.getZ()); -+ final int x = GeoEngine.getWorldX(gx); -+ final int y = GeoEngine.getWorldY(gy); -+ final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); - - // north arrow -- Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); -+ Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); - 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); -@@ -147,7 +146,7 @@ - exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z); - - // east arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); - 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); -@@ -154,13 +153,13 @@ - exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z); - - // south arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); - 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, Cell.NSWE_WEST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); - 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); -@@ -188,15 +187,15 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_EAST; -+ return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_EAST; -+ return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; - } - else - { -- return Cell.NSWE_EAST; -+ return GeoStructure.CELL_FLAG_E; // Direction.EAST; - } - } - else if (x < lastX) // west -@@ -203,26 +202,27 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_WEST; -+ return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_WEST; -+ return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; - } - else - { -- return Cell.NSWE_WEST; -+ return GeoStructure.CELL_FLAG_W; // Direction.WEST; - } - } -- else // unchanged x -+ else -+ // unchanged x - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH; -+ return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH; -+ return GeoStructure.CELL_FLAG_N; // Direction.NORTH; - } - else - { -Index: java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java -=================================================================== ---- java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (nonexistent) -+++ java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (working copy) -@@ -0,0 +1,386 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.tools.geodataconverter; -+ -+import java.io.BufferedOutputStream; -+import java.io.File; -+import java.io.FileOutputStream; -+import java.io.RandomAccessFile; -+import java.nio.ByteOrder; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.Scanner; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.commons.util.PropertiesParser; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoDataConverter -+{ -+ private static GeoFormat _format; -+ private static ABlock[][] _blocks; -+ -+ public static void main(String[] args) -+ { -+ // initialize config -+ loadGeoengineConfigs(); -+ -+ // get geodata format -+ String type = ""; -+ try (Scanner scn = new Scanner(System.in)) -+ { -+ while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) -+ { -+ System.out.println("GeoDataConverter: Select source geodata type:"); -+ System.out.println(" J: L2J (e.g. 23_22.l2j)"); -+ System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); -+ System.out.println(" E: Exit"); -+ System.out.print("Choice: "); -+ type = scn.next(); -+ } -+ } -+ if (type.equalsIgnoreCase("E")) -+ { -+ System.exit(0); -+ } -+ -+ _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; -+ -+ // start conversion -+ System.out.println("GeoDataConverter: Converting all " + _format + " files."); -+ -+ // initialize geodata container -+ _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files -+ int converted = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) -+ { -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) -+ { -+ final String input = String.format(_format.getFilename(), rx, ry); -+ final String filepath = Config.GEODATA_PATH; -+ final File f = new File(filepath + input); -+ if (f.exists() && !f.isDirectory()) -+ { -+ // load geodata -+ if (!loadGeoBlocks(input)) -+ { -+ System.out.println("GeoDataConverter: Unable to load " + input + " region file."); -+ continue; -+ } -+ -+ // recalculate nswe -+ if (!recalculateNswe()) -+ { -+ System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); -+ continue; -+ } -+ -+ // save geodata -+ final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); -+ if (!saveGeoBlocks(output)) -+ { -+ System.out.println("GeoDataConverter: Unable to save " + output + " region file."); -+ continue; -+ } -+ -+ converted++; -+ System.out.println("GeoDataConverter: Created " + output + " region file."); -+ } -+ } -+ } -+ System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); -+ } -+ -+ /** -+ * Loads geo blocks from buffer of the region file. -+ * @param filename : The name of the to load. -+ * @return boolean : True when successful. -+ */ -+ private static boolean loadGeoBlocks(String filename) -+ { -+ // region file is load-able, try to load it -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) -+ { -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) -+ if (_format == GeoFormat.L2OFF) -+ { -+ for (int i = 0; i < 18; i++) -+ { -+ buffer.get(); -+ } -+ } -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ if (_format == GeoFormat.L2J) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2J: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2J: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ else -+ { -+ // get block type -+ final short type = buffer.getShort(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ default: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ if (buffer.remaining() > 0) -+ { -+ System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ return false; -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); -+ return false; -+ } -+ } -+ -+ /** -+ * Recalculate diagonal flags for the region file. -+ * @return boolean : True when successful. -+ */ -+ private static boolean recalculateNswe() -+ { -+ try -+ { -+ for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) -+ { -+ for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) -+ { -+ // get block -+ final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // skip flat blocks -+ if (block instanceof BlockFlat) -+ { -+ continue; -+ } -+ -+ // for complex and multilayer blocks go though all layers -+ short height = Short.MAX_VALUE; -+ int index; -+ while ((index = block.getIndexBelow(x, y, height)) != -1) -+ { -+ // get height and nswe -+ height = block.getHeight(index); -+ byte nswe = block.getNswe(index); -+ -+ // update nswe with diagonal flags -+ nswe = updateNsweBelow(x, y, height, nswe); -+ -+ // set nswe of the cell -+ block.setNswe(index, nswe); -+ } -+ } -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ /** -+ * Updates the NSWE flag with diagonal flags. -+ * @param x : Geodata X coordinate. -+ * @param y : Geodata Y coordinate. -+ * @param z : Geodata Z coordinate. -+ * @param nsweValue : NSWE flag to be updated. -+ * @return byte : Updated NSWE flag. -+ */ -+ private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) -+ { -+ byte nswe = nsweValue; -+ -+ // calculate virtual layer height -+ final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); -+ -+ // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) -+ final byte nsweN = getNsweBelow(x, y - 1, height); -+ final byte nsweS = getNsweBelow(x, y + 1, height); -+ final byte nsweW = getNsweBelow(x - 1, y, height); -+ final byte nsweE = getNsweBelow(x + 1, y, height); -+ -+ // north-west -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NW; -+ } -+ -+ // north-east -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NE; -+ } -+ -+ // south-west -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SW; -+ } -+ -+ // south-east -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SE; -+ } -+ -+ return nswe; -+ } -+ -+ private static byte getNsweBelow(int geoX, int geoY, short worldZ) -+ { -+ // out of geo coordinates -+ if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) -+ { -+ return 0; -+ } -+ -+ // out of geo coordinates -+ if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) -+ { -+ return 0; -+ } -+ -+ // get block -+ final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // get index, when valid, return nswe -+ final int index = block.getIndexBelow(geoX, geoY, worldZ); -+ return index == -1 ? 0 : block.getNswe(index); -+ } -+ -+ /** -+ * Save region file to file. -+ * @param filename : The name of file to save. -+ * @return boolean : True when successful. -+ */ -+ private static boolean saveGeoBlocks(String filename) -+ { -+ try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) -+ { -+ // loop over region blocks and save each block -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[ix][iy].saveBlock(bos); -+ } -+ } -+ -+ // flush data to file -+ bos.flush(); -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ private static void loadGeoengineConfigs() -+ { -+ final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); -+ Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); -+ Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); -+ Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); -+ Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); -+ Config.PATHFINDING = geoData.getBoolean("PathFinding", true); -+ Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -+ Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); -+ Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); -+ Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); -+ Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); -+ Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); -+ } -+} -\ No newline at end of file diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/geodata/Readme.txt b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/geodata/Readme.txt index a480c8c380..2611882e56 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/geodata/Readme.txt +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/geodata/Readme.txt @@ -23,8 +23,8 @@ a - Prerequisites b - Make it work ---------------------------------------------- -To make geodata working: -* unpack your geodata files into "/data/geodata" folder -* open "/config/GeoEngine.ini" with your favorite text editor and then edit following config: - - CoordSynchronize = 2 -* If you do not use any geodata files, the server will automatically change this setting to -1. +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. diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java index 941690319d..1d1149d6b2 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/BoyAndGirl.java @@ -78,7 +78,7 @@ public class BoyAndGirl extends AbstractNpcAI startQuestTimer("NPC_SHOUT", 10000 + (getRandom(5) * 1000), npc, null); npc.setRunning(); final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 200, 600); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); return super.onSpawn(npc); } @@ -86,7 +86,7 @@ public class BoyAndGirl extends AbstractNpcAI public void onMoveFinished(Npc npc) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 200, 600); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); super.onMoveFinished(npc); } diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java index 558f2631e6..3bcf3810cb 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Devno.java @@ -48,7 +48,7 @@ public class Devno extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java index 7525cc99e8..ce81f5c05c 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Eleve.java @@ -48,7 +48,7 @@ public class Eleve extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java index 7d9150b156..f4e942c326 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Handermonkey.java @@ -46,7 +46,7 @@ public class Handermonkey extends AbstractNpcAI { final int x = npc.getSpawn().getX() + (getRandom(-100, 100)); final int y = npc.getSpawn().getY() + (getRandom(-100, 100)); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x, y, npc.getZ(), npc.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x, y, npc.getZ(), npc.getInstanceWorld()); addMoveToDesire(npc, loc, 0); } else diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java index c993ce8936..7df61da041 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Karonf.java @@ -48,7 +48,7 @@ public class Karonf extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java index 6e8cbee920..589d376f6b 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Marsha.java @@ -48,7 +48,7 @@ public class Marsha extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java index c616abccc6..5c09d531c8 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Morgan.java @@ -48,7 +48,7 @@ public class Morgan extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", (10 + getRandom(5)) * 1000, npc, null); } diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java index 3652b4ab02..fe20952121 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/areas/TalkingIsland/Rubentis.java @@ -48,7 +48,7 @@ public class Rubentis extends AbstractNpcAI if (getRandomBoolean()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, 500); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getLocation().getX(), npc.getLocation().getY(), npc.getLocation().getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("NPC_MOVE", 10000 + (getRandom(5) * 1000), npc, null); } diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java index beece64521..b0944f763d 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java @@ -63,7 +63,7 @@ public class Vortex extends AbstractNpcAI final int x = (int) (attackers.getX() + (600 * Math.cos(radians))); final int y = (int) (attackers.getY() + (600 * Math.sin(radians))); final int z = attackers.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(attackers.getX(), attackers.getY(), attackers.getZ(), x, y, z, attackers.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(attackers.getX(), attackers.getY(), attackers.getZ(), x, y, z, attackers.getInstanceWorld()); attackers.broadcastPacket(new FlyToLocation(attackers, x, y, z, FlyToLocation.FlyType.THROW_UP, 800, 800, 800)); attackers.setXYZ(loc); attackers.broadcastPacket(new ValidateLocation(attackers)); diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/others/FleeMonsters.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/others/FleeMonsters.java index bdbb61d8ec..def15d8afa 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/others/FleeMonsters.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/ai/others/FleeMonsters.java @@ -67,7 +67,7 @@ public class FleeMonsters extends AbstractNpcAI final int posX = (int) (npc.getX() + (FLEE_DISTANCE * Math.cos(radians))); final int posY = (int) (npc.getY() + (FLEE_DISTANCE * Math.sin(radians))); final int posZ = npc.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, attacker.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); return super.onAttack(npc, attacker, damage, isSummon); } diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index 524652cbc1..18511c6541 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { @@ -133,8 +133,8 @@ public class AdminGeodata implements IAdminCommandHandler } case "admin_geomap": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y + " (" + ((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + "," + ((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + " to " + ((((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + "," + ((((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + ")"); break; } diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java index f3dffa1227..76572f7f25 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java @@ -57,8 +57,8 @@ public class AdminMissingHtmls implements IAdminCommandHandler { case "admin_geomap_missing_htmls": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final int topLeftX = (x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE; final int topLeftY = (y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE; final int bottomRightX = (((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1; diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index fdb2319138..a0a34f1051 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -19,9 +19,9 @@ package handlers.admincommandhandlers; import java.util.List; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; +import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.util.BuilderUtil; @@ -44,13 +44,13 @@ public class AdminPathNode implements IAdminCommandHandler } if (activeChar.getTarget() != null) { - final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { BuilderUtil.sendSysMessage(activeChar, "No Route!"); return true; } - for (AbstractNodeLoc a : path) + for (Location a : path) { BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/Blink.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/Blink.java index 22f2c84eae..22697e8e20 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/Blink.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/Blink.java @@ -88,7 +88,7 @@ public class Blink extends AbstractEffect final int y = effected.getY() + y1; final int z = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, destination, _flyType, _flySpeed, _flyDelay, _animationSpeed)); diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/Fear.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/Fear.java index 95a5afdf98..88c795e04b 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/Fear.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/Fear.java @@ -100,7 +100,7 @@ public class Fear extends AbstractEffect final int posY = (int) (effected.getY() + (FEAR_RANGE * Math.sin(radians))); final int posZ = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); } } diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java index fddd94c60a..55c2e78d8a 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java @@ -57,7 +57,7 @@ public class FlyAway extends AbstractEffect final int y = (int) (effector.getY() - (nRadius * (dy / distance))); final int z = effector.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.broadcastPacket(new FlyToLocation(effected, destination, FlyType.THROW_UP)); effected.setXYZ(destination); diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java index 3c3f9aca62..14809ac499 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java @@ -179,7 +179,7 @@ public class KnockBack extends AbstractEffect final int x = (int) (effected.getX() + (_distance * Math.cos(radians))); final int y = (int) (effected.getY() + (_distance * Math.sin(radians))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, loc, _type, _speed, _delay, _animationSpeed)); diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/PullBack.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/PullBack.java index ed6a37eff6..38fe2fe9d0 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/PullBack.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/PullBack.java @@ -73,7 +73,7 @@ public class PullBack extends AbstractEffect } // In retail, you get debuff, but you are not even moved if there is obstacle. You are still disabled from using skills and moving though. - if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effector.getInstanceWorld())) + if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effected.getInstanceWorld())) { effected.broadcastPacket(new FlyToLocation(effected, effector, _type, _speed, _delay, _animationSpeed)); effected.setXYZ(effector.getX(), effector.getY(), GeoEngine.getInstance().getHeight(effector.getX(), effector.getY(), effector.getZ()) + 10); diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java index d6f9664141..813e496c47 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java @@ -87,7 +87,7 @@ public class TeleportToSummon extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = summon.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java index d3c263978c..c9dc34ac22 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java @@ -76,7 +76,7 @@ public class TeleportToTarget extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java index f2148adb54..c7ca6f64cb 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java @@ -65,7 +65,7 @@ public class RollingDice implements IItemHandler final int x = player.getX() + x1; final int y = player.getY() + y1; final int z = player.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); Broadcast.toSelfAndKnownPlayers(player, new Dice(player.getObjectId(), itemId, number, destination.getX(), destination.getY(), destination.getZ())); final SystemMessage sm = new SystemMessage(SystemMessageId.C1_HAS_ROLLED_A_S2); diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/targethandlers/Ground.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/targethandlers/Ground.java index acce8f1b2f..1328ff01b5 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/targethandlers/Ground.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/dist/game/data/scripts/handlers/targethandlers/Ground.java @@ -52,7 +52,7 @@ public class Ground implements ITargetTypeHandler return null; } - if (!GeoEngine.getInstance().canSeeTarget(creature, worldPosition)) + if (!GeoEngine.getInstance().canSeeLocation(creature, worldPosition)) { if (sendMessage) { diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/Config.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/Config.java index a11d851ac7..8ca3b9544b 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/Config.java @@ -61,6 +61,7 @@ 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; @@ -967,12 +968,17 @@ public class Config // GeoEngine // -------------------------------------------------- public static Path GEODATA_PATH; + public static GeoType GEODATA_TYPE; public static boolean PATHFINDING; public static String PATHFIND_BUFFERS; - public static float LOW_WEIGHT; - public static float MEDIUM_WEIGHT; - public static float HIGH_WEIGHT; - public static float DIAGONAL_WEIGHT; + public static int MOVE_WEIGHT; + public static int MOVE_WEIGHT_DIAG; + public static int OBSTACLE_WEIGHT; + 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; /** Attribute System */ public static int S_WEAPON_STONE; @@ -2567,12 +2573,17 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); + GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); - MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); - HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); - DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); + MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); + OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); + HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); + MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); + MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); // Load AllowedPlayerRaces config file (if exists) final PropertiesParser AllowedPlayerRaces = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE); diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/commons/util/CommonUtil.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/commons/util/CommonUtil.java index 14ae3a4560..ab7837d827 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/commons/util/CommonUtil.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/commons/util/CommonUtil.java @@ -584,4 +584,15 @@ public class CommonUtil final DecimalFormat formatter = new DecimalFormat(format, new DecimalFormatSymbols(Locale.ENGLISH)); return formatter.format(value); } + + /** + * @param numToTest : The number to test. + * @param min : The minimum limit. + * @param max : The maximum limit. + * @return the number or one of the limit (mininum / maximum). + */ + public static int limit(int numToTest, int min, int max) + { + return (numToTest > max) ? max : ((numToTest < min) ? min : numToTest); + } } diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/commons/util/Point2D.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/commons/util/Point2D.java new file mode 100644 index 0000000000..ebb6272e5e --- /dev/null +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/commons/util/Point2D.java @@ -0,0 +1,180 @@ +/* + * 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 . + */ +package org.l2jmobius.commons.util; + +import java.util.Objects; + +/** + * A datatype used to retain a 2D (x/y) point. It got the capability to be set and cleaned. + */ +public class Point2D +{ + protected volatile int _x; + protected volatile int _y; + + public Point2D(int x, int y) + { + _x = x; + _y = y; + } + + @Override + public Point2D clone() + { + return new Point2D(_x, _y); + } + + @Override + public String toString() + { + return _x + ", " + _y; + } + + @Override + public int hashCode() + { + return Objects.hash(_x, _y); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final Point2D other = (Point2D) obj; + return (_x == other._x) && (_y == other._y); + } + + /** + * @param x : The X coord to test. + * @param y : The Y coord to test. + * @return True if all coordinates equals this {@link Point2D} coordinates. + */ + public boolean equals(int x, int y) + { + return (_x == x) && (_y == y); + } + + public int getX() + { + return _x; + } + + public void setX(int x) + { + _x = x; + } + + public int getY() + { + return _y; + } + + public void setY(int y) + { + _y = y; + } + + public void set(int x, int y) + { + _x = x; + _y = y; + } + + /** + * Refresh the current {@link Point2D} using a reference {@link Point2D} and a distance. The new destination is calculated to go in opposite side of the {@link Point2D} reference.
+ *
+ * This method is perfect to calculate fleeing characters position. + * @param referenceLoc : The Point2D used as position. + * @param distance : The distance to be set between current and new position. + */ + public void setFleeing(Point2D referenceLoc, int distance) + { + final double xDiff = referenceLoc.getX() - _x; + final double yDiff = referenceLoc.getY() - _y; + + final double yxRation = Math.abs(xDiff / yDiff); + + final int y = (int) (distance / (yxRation + 1)); + final int x = (int) (y * yxRation); + + _x += (xDiff < 0 ? x : -x); + _y += (yDiff < 0 ? y : -y); + } + + public void clean() + { + _x = 0; + _y = 0; + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @return The distance between this {@Point2D} and some given coordinates. + */ + public double distance2D(int x, int y) + { + final double dx = (double) _x - x; + final double dy = (double) _y - y; + + return Math.sqrt((dx * dx) + (dy * dy)); + } + + /** + * @param point : The {@link Point2D} to test. + * @return The distance between this {@Point2D} and the {@link Point2D} set as parameter. + */ + public double distance2D(Point2D point) + { + return distance2D(point.getX(), point.getY()); + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of some given coordinates. + */ + public boolean isIn2DRadius(int x, int y, int radius) + { + return distance2D(x, y) < radius; + } + + /** + * @param point : The Point2D to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of the {@link Point2D} set as parameter. + */ + public boolean isIn2DRadius(Point2D point, int radius) + { + return distance2D(point) < radius; + } +} \ No newline at end of file diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/ai/AttackableAI.java index 547963e3e8..dc29d4cf6e 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/ai/AttackableAI.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/ai/AttackableAI.java @@ -578,7 +578,7 @@ public class AttackableAI extends CreatureAI } // Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast) - final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); + final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); moveTo(moveLoc.getX(), moveLoc.getY(), moveLoc.getZ()); } } @@ -801,7 +801,7 @@ public class AttackableAI extends CreatureAI final int newZ = npc.getZ() + 30; // Mobius: Verify destination. Prevents wall collision issues and fixes monsters not avoiding obstacles. - moveTo(GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); + moveTo(GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); } return; } diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/data/SpawnTable.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/data/SpawnTable.java index a81201f69e..19f0db5b3e 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/data/SpawnTable.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/data/SpawnTable.java @@ -114,8 +114,8 @@ public class SpawnTable } // XML file for spawn - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); // Write info to XML @@ -192,8 +192,8 @@ public class SpawnTable if (update) { - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = spawn.getNpcSpawnTemplate() != null ? spawn.getNpcSpawnTemplate().getSpawnTemplate().getFile() : new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); final File tempFile = new File(spawnFile.getAbsolutePath().substring(Config.DATAPACK_ROOT.getAbsolutePath().length() + 1).replace('\\', '/') + ".tmp"); try diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/enums/GeoType.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/enums/GeoType.java new file mode 100644 index 0000000000..f77aa5eac4 --- /dev/null +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/enums/GeoType.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.enums; + +public enum GeoType +{ + L2J("%d_%d.l2j"), + L2OFF("%d_%d_conv.dat"); + + private final String _filename; + + private GeoType(String filename) + { + _filename = filename; + } + + public String getFilename() + { + return _filename; + } +} \ No newline at end of file diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java new file mode 100644 index 0000000000..e62f50a8df --- /dev/null +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java @@ -0,0 +1,144 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 9d255a5f54..7bef9c770b 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,72 +16,63 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.channels.FileChannel.MapMode; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.logging.Level; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; import java.util.logging.Logger; import org.l2jmobius.Config; +import org.l2jmobius.commons.util.CommonUtil; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; -import org.l2jmobius.gameserver.geoengine.geodata.IRegion; -import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; -import org.l2jmobius.gameserver.geoengine.geodata.Region; +import org.l2jmobius.gameserver.enums.GeoType; +import org.l2jmobius.gameserver.enums.MoveDirectionType; +import org.l2jmobius.gameserver.geoengine.geodata.ABlock; +import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; +import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; +import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; +import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.model.interfaces.ILocational; -import org.l2jmobius.gameserver.util.GeoUtils; -import org.l2jmobius.gameserver.util.LinePointIterator; -import org.l2jmobius.gameserver.util.LinePointIterator3D; -/** - * @author -Nemesiss-, HorridoJoho - */ public class GeoEngine { - private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - - private static final int WORLD_MIN_X = -655360; - private static final int WORLD_MIN_Y = -589824; - private static final int WORLD_MIN_Z = -16384; - - /** 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; - - /** The regions array */ - private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; - private static final int ELEVATED_SEE_OVER_DISTANCE = 2; - private static final int MAX_SEE_OVER_HEIGHT = 48; - protected GeoEngine() + private final ABlock[][] _blocks; + private final BlockNull _nullBlock; + + // Pre-allocated buffers. + private final BufferHolder[] _buffers; + + public GeoEngine() { LOGGER.info("GeoEngine: Initializing..."); - for (int i = 0; i < _regions.length(); i++) - { - _regions.set(i, NullRegion.INSTANCE); - } + // Initialize block container. + _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; + + // Load null block. + _nullBlock = new BlockNull(); + + // Initialize multilayer temporarily buffer. + BlockMultilayer.initialize(); + + // Load geo files according to geoengine config setup. int loaded = 0; try { @@ -92,23 +83,52 @@ public class GeoEngine final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); if (Files.exists(geoFilePath)) { - try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + // Region file is load-able, try to load it. + if (loadGeoBlocks(regionX, regionY)) { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); loaded++; } } + else + { + // Region file is not load-able, load null blocks. + loadNullBlocks(regionX, regionY); + } } } } catch (Exception e) { - LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + LOGGER.warning("GeoEngine: Failed to load geodata! " + e); System.exit(1); } - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + // Release multilayer block temporarily buffer. + BlockMultilayer.release(); + + String[] array = Config.PATHFIND_BUFFERS.split(";"); + _buffers = new BufferHolder[array.length]; + + int count = 0; + for (int i = 0; i < array.length; i++) + { + String buf = array[i]; + String[] args = buf.split("x"); + + try + { + int size = Integer.parseInt(args[1]); + count += size; + _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); + } + catch (Exception e) + { + LOGGER.warning("Could not load buffer setting:" + buf + ". " + e); + } + } + LOGGER.info("Loaded " + count + " node buffers."); + // Avoid wrong configs when no files are loaded. if ((loaded == 0) && Config.PATHFINDING) { @@ -118,616 +138,913 @@ public class GeoEngine } /** - * @param geoX - * @param geoY - * @return the region + * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer and log this situation. + * @param size : pre-calculated minimal required size + * @return NodeBuffer : buffer */ - private IRegion getRegion(int geoX, int geoY) + private NodeBuffer getBuffer(int size) { - final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); - if ((region < 0) || (region >= _regions.length())) + NodeBuffer current = null; + for (BufferHolder holder : _buffers) { - return null; - } - return _regions.get(region); - } - - /** - * @param filePath - * @param regionX - * @param regionY - * @throws IOException - */ - 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()).load().order(ByteOrder.LITTLE_ENDIAN))); - } - } - - /** - * @param regionX - * @param regionY - */ - public void unloadRegion(int regionX, int regionY) - { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); - } - - /** - * @param geoX - * @param geoY - * @return if geodata exist - */ - public boolean hasGeoPos(int geoX, int geoY) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return false; - } - return region.hasGeo(); - } - - /** - * Checks the specified position for available geodata. - * @param x the world x - * @param y the world y - * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise - */ - public boolean hasGeo(int x, int y) - { - return hasGeoPos(getGeoX(x), getGeoY(y)); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe check - */ - public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return true; - } - return region.checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe anti-corner cut - */ - public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) - { - boolean can = true; - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - return can && checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the nearest Z value - */ - public int getNearestZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNearestZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next lower Z value - */ - public int getNextLowerZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextLowerZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next higher Z value - */ - public int getNextHigherZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextHigherZ(geoX, geoY, worldZ); - } - - /** - * Gets the Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHeight(int x, int y, int z) - { - return getNearestZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next lower Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getLowerHeight(int x, int y, int z) - { - return getNextLowerZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next higher Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHigherHeight(int x, int y, int z) - { - return getNextHigherZ(getGeoX(x), getGeoY(y), z); - } - - /** - * @param worldX - * @return the geo X - */ - public int getGeoX(int worldX) - { - return (worldX - WORLD_MIN_X) / 16; - } - - /** - * @param worldY - * @return the geo Y - */ - public int getGeoY(int worldY) - { - return (worldY - WORLD_MIN_Y) / 16; - } - - /** - * @param worldZ - * @return the geo Z - */ - public int getGeoZ(int worldZ) - { - return (worldZ - WORLD_MIN_Z) / 16; - } - - /** - * @param geoX - * @return the world X - */ - public int getWorldX(int geoX) - { - return (geoX * 16) + WORLD_MIN_X + 8; - } - - /** - * @param geoY - * @return the world Y - */ - public int getWorldY(int geoY) - { - return (geoY * 16) + WORLD_MIN_Y + 8; - } - - /** - * @param geoZ - * @return the world Z - */ - public int getWorldZ(int geoZ) - { - return (geoZ * 16) + WORLD_MIN_Z + 8; - } - - /** - * Can see target. Doors as target always return true. Checks doors between. - * @param cha the character - * @param target the target - * @return {@code true} if the character can see the target (LOS), {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, WorldObject target) - { - if (target.isDoor()) - { - // Can always see doors. - return true; - } - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); - } - - /** - * Can see target. Checks doors between. - * @param cha the character - * @param worldPosition the world position - * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) - { - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param world - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @param tworld the target's instanceId - * @return - */ - public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) - { - return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instance - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) - { - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) - { - return false; - } - return canSeeTarget(x, y, z, tx, ty, tz); - } - - /** - * @param prevX - * @param prevY - * @param prevGeoZ - * @param curX - * @param curY - * @param nswe - * @return the LOS Z value - */ - private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) - { - if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) - { - throw new RuntimeException("Multiple directions!"); - } - - if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) - { - return getNearestZ(curX, curY, prevGeoZ); - } - - return getNextHigherZ(curX, curY, prevGeoZ); - } - - /** - * Can see target. Does not check doors between. - * @param xValue the x coordinate - * @param yValue the y coordinate - * @param zValue the z coordinate - * @param txValue the target's x coordinate - * @param tyValue the target's y coordinate - * @param tzValue the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) - { - int x = xValue; - int y = yValue; - int tx = txValue; - int ty = tyValue; - - int geoX = getGeoX(x); - int geoY = getGeoY(y); - int tGeoX = getGeoX(tx); - int tGeoY = getGeoY(ty); - - int z = getNearestZ(geoX, geoY, zValue); - int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - // Fastpath. - if ((geoX == tGeoX) && (geoY == tGeoY)) - { - if (hasGeoPos(tGeoX, tGeoY)) - { - return z == tz; - } - return true; - } - - if (tz > z) - { - int tmp = tx; - tx = x; - x = tmp; - - tmp = ty; - ty = y; - y = tmp; - - tmp = tz; - tz = z; - z = tmp; - - tmp = tGeoX; - tGeoX = geoX; - geoX = tmp; - - tmp = tGeoY; - tGeoY = geoY; - geoY = tmp; - } - - final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); - // First point is guaranteed to be available, skip it, we can always see our own position. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - final int prevZ = pointIter.z(); - int prevGeoZ = prevZ; - int ptIndex = 0; - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - - if ((curX == prevX) && (curY == prevY)) + // Find proper size of buffer. + if (holder._size < size) { continue; } - final int beeCurZ = pointIter.z(); - int curGeoZ = prevGeoZ; - - // Check if the position has geodata. - if (hasGeoPos(curX, curY)) + // Find unlocked NodeBuffer. + for (NodeBuffer buffer : holder._buffer) { - final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); - curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); - int maxHeight; - if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) + if (!buffer.isLocked()) { - maxHeight = z + MAX_SEE_OVER_HEIGHT; - } - else - { - maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; + continue; } - boolean canSeeThrough = false; - if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) + return buffer; + } + + LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); + + // NodeBuffer not found, allocate temporary buffer. + current = new NodeBuffer(holder._size); + current.isLocked(); + } + + return current; + } + + /** + * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + * @return boolean : True, when geodata file was loaded without problem. + */ + private boolean loadGeoBlocks(int regionX, int regionY) + { + final String filename = String.format(Config.GEODATA_TYPE.getFilename(), regionX, regionY); + final String filepath = Config.GEODATA_PATH + "\\" + filename; + + // Standard load. + try (RandomAccessFile raf = new RandomAccessFile(filepath, "r"); + FileChannel fc = raf.getChannel()) + { + // Initialize file buffer. + MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // Load 18B header for L2OFF geodata (1st and 2nd byte...region X and Y). + if (Config.GEODATA_TYPE == GeoType.L2OFF) + { + for (int i = 0; i < 18; i++) { - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) + buffer.get(); + } + } + + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Loop over region blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + if (Config.GEODATA_TYPE == GeoType.L2J) { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) - { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); + // Get block type. + final byte type = buffer.get(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + case GeoStructure.TYPE_MULTILAYER_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + default: + { + throw new IllegalArgumentException("Unknown block type: " + type); + } + } } else { - canSeeThrough = true; + // Get block type. + final short type = buffer.getShort(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + default: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + } } } + } + + // Check data consistency. + if (buffer.remaining() > 0) + { + LOGGER.warning("Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); + } + + // Loading was successful. + return true; + } + catch (Exception e) + { + // An error occured while loading, load null blocks. + LOGGER.warning("Error loading " + filename + " region file. " + e); + + // Replace whole region file with null blocks. + loadNullBlocks(regionX, regionY); + + // Loading was not successful. + return false; + } + } + + /** + * Loads null blocks. Used when no region file is detected or an error occurs during loading. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + */ + private void loadNullBlocks(int regionX, int regionY) + { + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Load all null blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + _blocks[blockX + ix][blockY + iy] = _nullBlock; + } + } + } + + /** + * Converts world X to geodata X. + * @param worldX + * @return int : Geo X + */ + public static int getGeoX(int worldX) + { + return (worldX - World.WORLD_X_MIN) >> 4; + } + + /** + * Converts world Y to geodata Y. + * @param worldY + * @return int : Geo Y + */ + public static int getGeoY(int worldY) + { + return (worldY - World.WORLD_Y_MIN) >> 4; + } + + /** + * Converts geodata X to world X. + * @param geoX + * @return int : World X + */ + public static int getWorldX(int geoX) + { + return (geoX << 4) + World.WORLD_X_MIN + 8; + } + + /** + * Converts geodata Y to world Y. + * @param geoY + * @return int : World Y + */ + public static int getWorldY(int geoY) + { + return (geoY << 4) + World.WORLD_Y_MIN + 8; + } + + /** + * Returns block of geodata on given coordinates. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return {@link ABlock} : Block of geodata. + */ + public ABlock getBlock(int geoX, int geoY) + { + final int x = geoX / GeoStructure.BLOCK_CELLS_X; + if ((x < 0) || (x >= GeoStructure.GEO_BLOCKS_X)) + { + return null; + } + final int y = geoY / GeoStructure.BLOCK_CELLS_Y; + if ((y < 0) || (y >= GeoStructure.GEO_BLOCKS_Y)) + { + return null; + } + return _blocks[x][y]; + } + + /** + * Check if geo coordinates has geo. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return boolean : True, if given geo coordinates have geodata + */ + public boolean hasGeoPos(int geoX, int geoY) + { + final ABlock block = getBlock(geoX, geoY); + return (block != null) && block.hasGeoPos(); + } + + /** + * Returns the height of cell, which is closest to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell geodata Z coordinate, closest to given coordinates. + */ + public short getHeightNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return (short) worldZ; + } + return block.getHeightNearest(geoX, geoY, worldZ); + } + + /** + * Returns the NSWE flag byte of cell, which is closes to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. + */ + public byte getNsweNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return GeoStructure.CELL_FLAG_ALL; + } + return block.getNsweNearest(geoX, geoY, worldZ); + } + + /** + * Check if world coordinates has geo. + * @param worldX : World X + * @param worldY : World Y + * @return boolean : True, if given world coordinates have geodata + */ + public boolean hasGeo(int worldX, int worldY) + { + return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param loc : The location used as reference. + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(Location loc) + { + return getHeightNearest(getGeoX(loc.getX()), getGeoY(loc.getY()), loc.getZ()); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param worldX : world x + * @param worldY : world y + * @param worldZ : world z + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(int worldX, int worldY, int worldZ) + { + return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); + } + + /** + * Check line of sight from {@link WorldObject} to {@link WorldObject}.
+ * @param object : The origin object. + * @param target : The target object. + * @return True, when object can see target. + */ + public boolean canSeeTarget(WorldObject object, WorldObject target) + { + // Can always see doors. + if (target.isDoor()) + { + return true; + } + + if (object.getInstanceWorld() != target.getInstanceWorld()) + { + return false; + } + + if (DoorData.getInstance().checkIfDoorsBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld())) + { + return false; + } + + // Get object's and target's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + double theight = 0; + if (target instanceof Creature) + { + theight += (((Creature) target).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + return canSee(object.getX(), object.getY(), object.getZ(), oheight, target.getX(), target.getY(), target.getZ(), theight) && canSee(target.getX(), target.getY(), target.getZ(), theight, object.getX(), object.getY(), object.getZ(), oheight); + } + + /** + * Check line of sight from {@link WorldObject} to {@link Location}.
+ * Note: The check uses {@link Location}'s real Z coordinate (e.g. point above ground), not its geodata representation. + * @param object : The origin object. + * @param position : The target position. + * @return True, when object can see position. + */ + public boolean canSeeLocation(WorldObject object, Location position) + { + // Get object and location coordinates. + int ox = object.getX(); + int oy = object.getY(); + int oz = object.getZ(); + int tx = position.getX(); + int ty = position.getY(); + int tz = position.getZ(); + + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld())) + { + return false; + } + + // Get object's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + // Perform geodata check. + return canSee(ox, oy, oz, oheight, tx, ty, tz, 0) && canSee(tx, ty, tz, 0, ox, oy, oz, oheight); + } + + /** + * Simple check for origin to target visibility.
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param oheight : The height of origin, used as start point. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param theight : The height of target, used as end point. + * @return True, when origin can see target. + */ + public boolean canSee(int ox, int oy, int oz, double oheight, int tx, int ty, int tz, double theight) + { + // Get origin geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get target geodata coordinates. + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check being on same cell and layer (index). + // Note: Get index must use origin height increased by cell height, the method returns index to height exclusive self. + int index = block.getIndexBelow(gox, goy, oz + GeoStructure.CELL_HEIGHT); + if (index < 0) + { + return false; + } + + if ((gox == gtx) && (goy == gty)) + { + return index == block.getIndexBelow(gtx, gty, tz + GeoStructure.CELL_HEIGHT); + } + + // Get ground and nswe flag. + int groundZ = block.getHeight(index); + int nswe = block.getNswe(index); + + // Get delta coordinates, slope of line (XY, XZ) and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double dz = (tz + theight) - (oz + oheight); + final double m = (double) dy / dx; + final double mz = dz / Math.sqrt((dx * dx) + (dy * dy)); + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); + + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) + { + // Set next cell in X direction. + gridX += mdt.getStepX(); + gox += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); - if (!canSeeThrough) - { - return false; - } + // Set next cell in Y direction. + gridY += mdt.getStepY(); + goy += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevGeoZ = curGeoZ; - ++ptIndex; + // Get block of the next cell. + block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get line of sight height (including Z slope). + double losz = oz + oheight + Config.MAX_OBSTACLE_HEIGHT; + losz += mz * Math.sqrt(((checkX - ox) * (checkX - ox)) + ((checkY - oy) * (checkY - oy))); + + // Check line of sight going though wall (vertical check). + + // Get index of particular layer, based on last iterated cell conditions. + boolean canMove = (nswe & dir) != 0; + if (canMove) + { + // No wall present, get next cell below current cell. + index = block.getIndexBelow(gox, goy, groundZ + GeoStructure.CELL_IGNORE_HEIGHT); + } + else + { + // Wall present, get next cell above current cell. + index = block.getIndexAbove(gox, goy, groundZ - (2 * GeoStructure.CELL_HEIGHT)); + } + + // Next cell's does not exist (no geodata with valid condition), return fail. + if (index < 0) + { + return false; + } + + // Get next cell's layer height. + int z = block.getHeight(index); + + // Perform sine of sight check (next cell is above line of sight line), return fail. + if (z > losz) + { + return false; + } + + // Next cell is accessible, update z and NSWE. + groundZ = z; + nswe = block.getNswe(index); } + // Iteration is completed, no obstacle is found. return true; } /** - * Move check. - * @param x the x coordinate - * @param y the y coordinate - * @param zValue the z coordinate - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tzValue the target's z coordinate - * @param instance the instance - * @return the last Location (x,y,z) where player can walk - just before wall + * Check movement from coordinates to coordinates.
+ * Note: The Z coordinates are supposed to be already validated geodata coordinates. + * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return True, when target coordinates are reachable from origin coordinates. */ - public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(x); - final int geoY = getGeoY(y); - final int z = getNearestZ(geoX, geoY, zValue); - final int tGeoX = getGeoX(tx); - final int tGeoY = getGeoY(ty); - final int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return new Location(x, y, getHeight(x, y, z)); - } - if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) - { - return new Location(x, y, getHeight(x, y, z)); + return false; } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = z; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return false; + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + int goz = getHeightNearest(gox, goy, oz); + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check movement within same cell. + if ((gox == gtx) && (goy == gty)) + { + return goz == getHeight(tx, ty, tz); + } + + // Get nswe flag. + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - // Can't move, return previous location. - return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check point heading into obstacle, if so return current point. + if ((nswe & dir) == 0) + { + return false; + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return false; + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != tz)) - { - // Different floors, return start location. - return new Location(x, y, z); - } - - return new Location(tx, ty, tz); + // When origin Z is target Z, the move is successful. + return goz == getHeight(tx, ty, tz); } /** - * Checks if its possible to move from one location to another. - * @param fromX the X coordinate to start checking from - * @param fromY the Y coordinate to start checking from - * @param fromZvalue the Z coordinate to start checking from - * @param toX the X coordinate to end checking at - * @param toY the Y coordinate to end checking at - * @param toZvalue the Z coordinate to end checking at - * @param instance the instance - * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + * Check movement from origin to target coordinates. Returns last available point in the checked path.
+ * Target X and Y reachable and Z is on same floor: + *
    + *
  • Location of the target with corrected Z value from geodata.
  • + *
+ * Target X and Y reachable but Z is on another floor: + *
    + *
  • Location of the origin with corrected Z value from geodata.
  • + *
+ * Target X and Y not reachable: + *
    + *
  • Last accessible location in destination to target.
  • + *
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return The {@link Location} representing last point of movement (e.g. just before wall). */ - public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + public Location getValidLocation(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(fromX); - final int geoY = getGeoY(fromY); - final int fromZ = getNearestZ(geoX, geoY, fromZvalue); - final int tGeoX = getGeoX(toX); - final int tGeoY = getGeoY(toY); - final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); - - if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) - { - return false; + return new Location(ox, oy, oz); } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = fromZ; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return new Location(ox, oy, oz); + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + final int gtz = getHeightNearest(gtx, gty, tz); + int goz = getHeightNearest(gox, goy, oz); + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - return false; - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check target cell is out of geodata grid (world coordinates). + if ((nx < 0) || (nx >= GeoStructure.GEO_CELLS_X) || (ny < 0) || (ny >= GeoStructure.GEO_CELLS_Y)) + { + return new Location(checkX, checkY, goz); + } + + // Check point heading into obstacle, if so return current (border) point. + if ((nswe & dir) == 0) + { + return new Location(checkX, checkY, goz); + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current (border) point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return new Location(checkX, checkY, goz); + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) - { - // Different floors. - return false; - } - - return true; + // Compare Z coordinates: + // If same, path is okay, return target point and fix its Z geodata coordinate. + // If not same, path is does not exist, return origin point. + return goz == gtz ? new Location(tx, ty, gtz) : new Location(ox, oy, oz); } + /** + * Returns the list of location objects as a result of complete path calculation. + * @param ox : origin x + * @param oy : origin y + * @param oz : origin z + * @param tx : target x + * @param ty : target y + * @param tz : target z + * @param instance + * @return {@code List} : complete path from nodes + */ + public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + { + // Get origin and check existing geo coords. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + if (!hasGeoPos(gox, goy)) + { + return Collections.emptyList(); + } + + int goz = getHeightNearest(gox, goy, oz); + + // Get target and check existing geo coords. + int gtx = getGeoX(tx); + int gty = getGeoY(ty); + if (!hasGeoPos(gtx, gty)) + { + return Collections.emptyList(); + } + + int gtz = getHeightNearest(gtx, gty, tz); + + // Prepare buffer for pathfinding calculations. + NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + if (buffer == null) + { + return Collections.emptyList(); + } + + // Find path. + List path = null; + try + { + path = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + if (path.isEmpty()) + { + return Collections.emptyList(); + } + } + catch (Exception e) + { + return Collections.emptyList(); + } + finally + { + buffer.free(); + } + + // Check path. + if (path.size() < 3) + { + return path; + } + + // Get path list iterator. + ListIterator point = path.listIterator(); + + // Get node A (origin). + int nodeAx = ox; + int nodeAy = oy; + int nodeAz = goz; + + // Get node B. + Location nodeB = point.next(); + + // Iterate thought the path to optimize it. + while (point.hasNext()) + { + // Get node C. + Location nodeC = path.get(point.nextIndex()); + + // Check movement from node A to node C. + if (canMoveToTarget(nodeAx, nodeAy, nodeAz, nodeC.getX(), nodeC.getY(), nodeC.getZ(), instance)) + { + // Can move from node A to node C. + // Remove node B. + point.remove(); + } + else + { + // Can not move from node A to node C. + // Set node A (node B is part of path, update A coordinates). + nodeAx = nodeB.getX(); + nodeAy = nodeB.getY(); + nodeAz = nodeB.getZ(); + } + + // Set node B. + nodeB = point.next(); + } + + return path; + } + + /** + * NodeBuffer container with specified size and count of separate buffers. + */ + private static class BufferHolder + { + final int _size; + final List _buffer; + + public BufferHolder(int size, int count) + { + _size = size; + _buffer = new ArrayList<>(count); + + for (int i = 0; i < count; i++) + { + _buffer.add(new NodeBuffer(size)); + } + } + } + + /** + * Returns the instance of the {@link GeoEngine}. + * @return {@link GeoEngine} : The instance. + */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -737,4 +1054,4 @@ public class GeoEngine { protected static final GeoEngine INSTANCE = new GeoEngine(); } -} +} \ No newline at end of file diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java deleted file mode 100644 index cc76b7523a..0000000000 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ /dev/null @@ -1,301 +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 . - */ -package org.l2jmobius.gameserver.geoengine; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; -import org.l2jmobius.gameserver.model.World; -import org.l2jmobius.gameserver.model.instancezone.Instance; - -/** - * @author -Nemesiss- - */ -public class GeoEnginePathfinding -{ - private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); - - private BufferInfo[] _buffers; - - protected GeoEnginePathfinding() - { - try - { - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - - _buffers = 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); - } - - _buffers[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"); - } - } - - public boolean pathNodesExist(short regionoffset) - { - return false; - } - - public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) - { - 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)))); - if (buffer == null) - { - return null; - } - - List path = null; - try - { - final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); - - if (result == null) - { - return null; - } - - path = constructPath(result); - } - catch (Exception e) - { - LOGGER.warning(e.getMessage()); - return null; - } - finally - { - buffer.free(); - } - - // check path - if (path.size() < 3) - { - return path; - } - - int currentX, currentY, currentZ; - ListIterator middlePoint; - - middlePoint = path.listIterator(); - currentX = x; - currentY = y; - currentZ = z; - - while (middlePoint.hasNext()) - { - final AbstractNodeLoc locMiddle = middlePoint.next(); - if (!middlePoint.hasNext()) - { - break; - } - - final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); - if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) - { - middlePoint.remove(); - } - else - { - currentX = locMiddle.getX(); - currentY = locMiddle.getY(); - currentZ = locMiddle.getZ(); - } - } - - return path; - } - - /** - * Convert geodata position to pathnode position - * @param geo_pos - * @return pathnode position - */ - public short getNodePos(int geo_pos) - { - return (short) (geo_pos >> 3); // OK? - } - - /** - * Convert node position to pathnode block position - * @param node_pos - * @return pathnode block position (0...255) - */ - public short getNodeBlock(int node_pos) - { - return (short) (node_pos % 256); - } - - public byte getRegionX(int node_pos) - { - return (byte) ((node_pos >> 8) + World.TILE_X_MIN); - } - - public byte getRegionY(int node_pos) - { - return (byte) ((node_pos >> 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 node_x rx - * @return - */ - public int calculateWorldX(short node_x) - { - return World.MAP_MIN_X + (node_x * 128) + 48; - } - - /** - * Convert pathnode y to World y position - * @param node_y - * @return - */ - public int calculateWorldY(short node_y) - { - return World.MAP_MIN_Y + (node_y * 128) + 48; - } - - private List constructPath(AbstractNode nodeValue) - { - final LinkedList path = new LinkedList<>(); - int previousDirectionX = Integer.MIN_VALUE; - int previousDirectionY = Integer.MIN_VALUE; - int directionX, directionY; - - AbstractNode node = nodeValue; - while (node.getParent() != null) - { - directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); - directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); - - // only add a new route point if moving direction changes - if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) - { - previousDirectionX = directionX; - previousDirectionY = directionY; - - path.addFirst(node.getLoc()); - node.setLoc(null); - } - - node = node.getParent(); - } - - return path; - } - - private CellNodeBuffer alloc(int size) - { - CellNodeBuffer current = null; - for (BufferInfo i : _buffers) - { - if (i.mapSize >= size) - { - for (CellNodeBuffer buf : i.buffer) - { - if (buf.lock()) - { - current = buf; - break; - } - } - if (current != null) - { - break; - } - - // not found, allocate temporary buffer - current = new CellNodeBuffer(i.mapSize); - current.lock(); - if (i.buffer.size() < i.count) - { - i.buffer.add(current); - break; - } - } - } - - return current; - } - - private static final class BufferInfo - { - final int mapSize; - final int count; - ArrayList buffer; - - public BufferInfo(int size, int cnt) - { - mapSize = size; - count = cnt; - buffer = new ArrayList<>(count); - } - } - - public static GeoEnginePathfinding getInstance() - { - return SingletonHolder.INSTANCE; - } - - private static class SingletonHolder - { - protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); - } -} diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java new file mode 100644 index 0000000000..49ec35aa1c --- /dev/null +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java @@ -0,0 +1,85 @@ +/* + * 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 . + */ +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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @param index : Index of the cell. + * @return short : Cell geodata Z coordinate, below given coordinates. + */ + public abstract byte getNswe(int index); +} \ No newline at end of file diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java new file mode 100644 index 0000000000..f5997bf287 --- /dev/null +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java @@ -0,0 +1,130 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java new file mode 100644 index 0000000000..9af9d2a28a --- /dev/null +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java @@ -0,0 +1,95 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java new file mode 100644 index 0000000000..31fbf5d477 --- /dev/null +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java new file mode 100644 index 0000000000..f77d21d84b --- /dev/null +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java @@ -0,0 +1,68 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +public class BlockNull extends ABlock +{ + @Override + public boolean hasGeoPos() + { + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java deleted file mode 100644 index 61ea4fcf82..0000000000 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java +++ /dev/null @@ -1,48 +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 . - */ -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() - { - } -} diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java deleted file mode 100644 index 3c8de1a7f7..0000000000 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java +++ /dev/null @@ -1,77 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java deleted file mode 100644 index 1ccdefe3da..0000000000 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java +++ /dev/null @@ -1,56 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java new file mode 100644 index 0000000000..691b164e0c --- /dev/null +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ +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_ALL = 0x0F; + + // 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; +} \ No newline at end of file diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java deleted file mode 100644 index 25eafc5a04..0000000000 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java +++ /dev/null @@ -1,42 +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 . - */ -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); -} diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java deleted file mode 100644 index 0d2c765002..0000000000 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java +++ /dev/null @@ -1,47 +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 . - */ -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(); -} diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java deleted file mode 100644 index 52df457360..0000000000 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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); - - // 1 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; - } -} diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java deleted file mode 100644 index 72a5a635c7..0000000000 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java +++ /dev/null @@ -1,55 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author HorridoJoho - */ -public final class NullRegion implements IRegion -{ - public static final NullRegion INSTANCE = new NullRegion(); - - @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 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; - } -} diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java deleted file mode 100644 index fc924cb875..0000000000 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java +++ /dev/null @@ -1,92 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java deleted file mode 100644 index 5087513ed9..0000000000 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java +++ /dev/null @@ -1,87 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public abstract class AbstractNode -{ - private T _loc; - private AbstractNode _parent; - - public AbstractNode(T loc) - { - _loc = loc; - } - - public void setParent(AbstractNode p) - { - _parent = p; - } - - public AbstractNode getParent() - { - return _parent; - } - - public T getLoc() - { - return _loc; - } - - public void setLoc(T l) - { - _loc = l; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); - return result; - } - - @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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java deleted file mode 100644 index 7223b5b2be..0000000000 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java +++ /dev/null @@ -1,33 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -/** - * @author -Nemesiss- - */ -public abstract class AbstractNodeLoc -{ - public abstract int getX(); - - public abstract int getY(); - - public abstract int getZ(); - - public abstract int getNodeX(); - - public abstract int getNodeY(); -} diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java deleted file mode 100644 index 3425234e0e..0000000000 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public class CellNode extends AbstractNode -{ - 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java deleted file mode 100644 index 522610bb7c..0000000000 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java +++ /dev/null @@ -1,348 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -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 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) - { - _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(); - } - - public final List debugPath() - { - final List 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 void getNeighbors() - { - if (!_current.getLoc().canGoAll()) - { - 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); - } - - // SouthEast - if ((nodeE != null) && (nodeS != null)) - { - if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) - { - addNode(x + 1, y + 1, z, true); - } - } - - // SouthWest - if ((nodeS != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) - { - addNode(x - 1, y + 1, z, true); - } - } - - // NorthEast - if ((nodeN != null) && (nodeE != null)) - { - if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) - { - addNode(x + 1, y - 1, z, true); - } - } - - // NorthWest - if ((nodeN != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) - { - addNode(x - 1, y - 1, z, true); - } - } - } - - private 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(); - // reinit node if needed - if (result.getLoc() != null) - { - result.getLoc().set(x, y, z); - } - else - { - result.setLoc(new NodeLoc(x, y, z)); - } - } - - return result; - } - - private 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 boolean isHighWeight(int x, int y, int z) - { - final CellNode result = getNode(x, y, z); - if (result == null) - { - return true; - } - - if (!result.getLoc().canGoAll()) - { - return true; - } - if (Math.abs(result.getLoc().getZ() - z) > 16) - { - return true; - } - - return false; - } - - private 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java new file mode 100644 index 0000000000..fa5ca43c2f --- /dev/null +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -0,0 +1,111 @@ +/* + * 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 . + */ +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 geodata values. + private int _geoX; + private int _geoY; + private byte _nswe; + + // 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; + + _costG = 0; + _costH = 0; + _costF = 0; + + _parent = null; + } + + public void setGeo(int gx, int gy, int gz, byte nswe) + { + super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); + + _geoX = gx; + _geoY = gy; + _nswe = nswe; + } + + 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 int getCostF() + { + return _costF; + } + + public Node getParent() + { + return _parent; + } + + @Override + public int compareTo(Node o) + { + return _costF - o._costF; + } +} \ No newline at end of file diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java new file mode 100644 index 0000000000..b464212c96 --- /dev/null +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -0,0 +1,368 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.awt.Color; +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; +import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; + +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 _opened; + // Container holding Nodes already explored. + private final List _closed; + + // Target coordinates. + private int _gtx; + private int _gty; + private int _gtz; + + // Pathfinding statistics. + private long _timeStamp; + private long _lastElapsedTime; + + 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 findPath(int gox, int goy, int goz, int gtx, int gty, int gtz) + { + // Set start timestamp. + _timeStamp = System.currentTimeMillis(); + + // 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)); + _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 constructPath() + { + // Create result. + final LinkedList 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; + } + + /** + * Creates list of Nodes to show debug path. + * @param debug : The debug packet to add debug informations in. + */ + public void debugPath(ExServerPrimitive debug) + { + // Add all opened node as yellow points. + for (Node n : _opened) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); + } + + // Add all opened node as blue points. + for (Node n : _closed) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); + } + } + + 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; + + _lastElapsedTime = System.currentTimeMillis() - _timeStamp; + _lock.unlock(); + } + + public long getElapsedTime() + { + return _lastElapsedTime; + } + + /** + * Expand the current {@link Node} by exploring its neighbors (axially and diagonally). + */ + private void expand() + { + // Movement is blocked, skip. + final byte nswe = _current.getNSWE(); + if (nswe == 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; + + // Can move north, expand. + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + } + + // Can move south, expand. + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + } + + // Can move west, expand. + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move east, expand. + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move north-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move north-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + } + + /** + * 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 weight : The weight of movement to the new node. + * @return The nswe of the added node. Blank, if not added. + */ + private byte addNode(int gx, int gy, int gzValue, int weight) + { + // 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]; + + // Set node geodata coordinates. + node.setGeo(gx, gy, gz, nswe); + + // 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, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + _opened.add(node); + _bufferIndex++; + return nswe; + } + + /** + * Calculate cost H value, calculated using diagonal distance method.
+ * 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. + final int dx = Math.abs(gx - _gtx); + final int dy = Math.abs(gy - _gty); + final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + + // Get diagonal and axial differences to the target. + final int dd = Math.min(dx, dy); + final int da = Math.max(dx, dy) - dd; + + // Calculate the diagonal distance of the node to the target. + return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + } +} \ No newline at end of file diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java deleted file mode 100644 index e3bad04b25..0000000000 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; - -/** - * @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 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 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; - // return super.hashCode(); - } - - @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; - if (_x != other._x) - { - return false; - } - if (_y != other._y) - { - return false; - } - if (_goNorth != other._goNorth) - { - return false; - } - if (_goEast != other._goEast) - { - return false; - } - if (_goSouth != other._goSouth) - { - return false; - } - if (_goWest != other._goWest) - { - return false; - } - if (_geoHeight != other._geoHeight) - { - return false; - } - return true; - } -} diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java index fec7e24f5e..c93649d8fd 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java @@ -94,15 +94,15 @@ public class ZoneManager implements IXmlReader private static final Map SETTINGS = new HashMap<>(); private static final int SHIFT_BY = 15; - private static final int OFFSET_X = Math.abs(World.MAP_MIN_X >> SHIFT_BY); - private static final int OFFSET_Y = Math.abs(World.MAP_MIN_Y >> SHIFT_BY); + private static final int OFFSET_X = Math.abs(World.WORLD_X_MIN >> SHIFT_BY); + private static final int OFFSET_Y = Math.abs(World.WORLD_Y_MIN >> SHIFT_BY); private final Map, ConcurrentHashMap> _classZones = new ConcurrentHashMap<>(); private final Map _spawnTerritories = new ConcurrentHashMap<>(); private final AtomicInteger _lastDynamicId = new AtomicInteger(300000); private List _debugItems; - private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.MAP_MAX_X >> SHIFT_BY) + OFFSET_X + 1][(World.MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y + 1]; + private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.WORLD_X_MAX >> SHIFT_BY) + OFFSET_X + 1][(World.WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y + 1]; /** * Instantiates a new zone manager. diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/Location.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/Location.java index 73689ab1a6..ca3b22a3f0 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/Location.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/Location.java @@ -16,30 +16,30 @@ */ package org.l2jmobius.gameserver.model; +import java.util.Objects; + +import org.l2jmobius.commons.util.Point2D; import org.l2jmobius.gameserver.model.interfaces.ILocational; import org.l2jmobius.gameserver.model.interfaces.IPositionable; /** - * Location data transfer object.
- * Contains coordinates data, heading and instance Id. - * @author Zoey76 + * A datatype used to retain a 3D (x/y/z/heading) point. It got the capability to be set and cleaned. */ -public class Location implements IPositionable +public class Location extends Point2D implements IPositionable { - protected int _x; - protected int _y; - protected int _z; - private int _heading; + protected volatile int _z; + protected volatile int _heading; public Location(int x, int y, int z) { - this(x, y, z, 0); + super(x, y); + _z = z; + _heading = 0; } public Location(int x, int y, int z, int heading) { - _x = x; - _y = y; + super(x, y); _z = z; _heading = heading; } @@ -51,9 +51,8 @@ public class Location implements IPositionable public Location(StatSet set) { - _x = set.getInt("x"); - _y = set.getInt("y"); - _z = set.getInt("z"); + super(set.getInt("x", 0), set.getInt("y", 0)); + _z = set.getInt("z", 0); _heading = set.getInt("heading", 0); } @@ -146,6 +145,25 @@ public class Location implements IPositionable _heading = loc.getHeading(); } + @Override + public void clean() + { + super.clean(); + _z = 0; + } + + @Override + public Location clone() + { + return new Location(_x, _y, _z); + } + + @Override + public int hashCode() + { + return (31 * super.hashCode()) + Objects.hash(_z); + } + @Override public boolean equals(Object obj) { diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/World.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/World.java index 4821081142..c2839ea6b9 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/World.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/World.java @@ -68,19 +68,19 @@ public class World public static final int TILE_Y_MAX = 26; public static final int TILE_ZERO_COORD_X = 20; public static final int TILE_ZERO_COORD_Y = 18; - public static final int MAP_MIN_X = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; - public static final int MAP_MIN_Y = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; + public static final int WORLD_X_MIN = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; + public static final int WORLD_Y_MIN = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; - public static final int MAP_MAX_X = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; - public static final int MAP_MAX_Y = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; + public static final int WORLD_X_MAX = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; + public static final int WORLD_Y_MAX = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; /** Calculated offset used so top left region is 0,0 */ - public static final int OFFSET_X = Math.abs(MAP_MIN_X >> SHIFT_BY); - public static final int OFFSET_Y = Math.abs(MAP_MIN_Y >> SHIFT_BY); + public static final int OFFSET_X = Math.abs(WORLD_X_MIN >> SHIFT_BY); + public static final int OFFSET_Y = Math.abs(WORLD_Y_MIN >> SHIFT_BY); /** Number of regions. */ - private static final int REGIONS_X = (MAP_MAX_X >> SHIFT_BY) + OFFSET_X; - private static final int REGIONS_Y = (MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y; + private static final int REGIONS_X = (WORLD_X_MAX >> SHIFT_BY) + OFFSET_X; + private static final int REGIONS_Y = (WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y; /** Map containing all the players in game. */ private static final Map _allPlayers = new ConcurrentHashMap<>(); diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/WorldObject.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/WorldObject.java index 0e5536bbc0..62313ffea1 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/WorldObject.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/WorldObject.java @@ -188,23 +188,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif synchronized (this) { int spawnX = x; - if (spawnX > World.MAP_MAX_X) + if (spawnX > World.WORLD_X_MAX) { - spawnX = World.MAP_MAX_X - 5000; + spawnX = World.WORLD_X_MAX - 5000; } - if (spawnX < World.MAP_MIN_X) + if (spawnX < World.WORLD_X_MIN) { - spawnX = World.MAP_MIN_X + 5000; + spawnX = World.WORLD_X_MIN + 5000; } int spawnY = y; - if (spawnY > World.MAP_MAX_Y) + if (spawnY > World.WORLD_Y_MAX) { - spawnY = World.MAP_MAX_Y - 5000; + spawnY = World.WORLD_Y_MAX - 5000; } - if (spawnY < World.MAP_MIN_Y) + if (spawnY < World.WORLD_Y_MIN) { - spawnY = World.MAP_MIN_Y + 5000; + spawnY = World.WORLD_Y_MIN + 5000; } // Set the x,y,z position of the WorldObject. If flagged with _isSpawned, setXYZ will automatically update world region, so avoid that. @@ -518,23 +518,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif public void setXYZInvisible(int x, int y, int z) { int correctX = x; - if (correctX > World.MAP_MAX_X) + if (correctX > World.WORLD_X_MAX) { - correctX = World.MAP_MAX_X - 5000; + correctX = World.WORLD_X_MAX - 5000; } - if (correctX < World.MAP_MIN_X) + if (correctX < World.WORLD_X_MIN) { - correctX = World.MAP_MIN_X + 5000; + correctX = World.WORLD_X_MIN + 5000; } int correctY = y; - if (correctY > World.MAP_MAX_Y) + if (correctY > World.WORLD_Y_MAX) { - correctY = World.MAP_MAX_Y - 5000; + correctY = World.WORLD_Y_MAX - 5000; } - if (correctY < World.MAP_MIN_Y) + if (correctY < World.WORLD_Y_MIN) { - correctY = World.MAP_MIN_Y + 5000; + correctY = World.WORLD_Y_MIN + 5000; } setXYZ(correctX, correctY, z); diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/actor/Creature.java index 7b79836e40..359bcb0d64 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -65,8 +65,6 @@ 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.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -2585,7 +2583,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3405,8 +3403,8 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe final int originalX = x; final int originalY = y; final int originalZ = z; - final int gtx = (originalX - World.MAP_MIN_X) >> 4; - final int gty = (originalY - World.MAP_MIN_Y) >> 4; + final int gtx = (originalX - World.WORLD_X_MIN) >> 4; + final int gty = (originalY - World.WORLD_Y_MIN) >> 4; if (isOnGeodataPath()) { try @@ -3429,7 +3427,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe && !(((curZ - z) > 300) && (distance < 300))) // Prohibit correcting destination if character wants to fall. { // location different if destination wasn't reached (or just z coord is different) - final Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(curX, curY, curZ, x, y, z, getInstanceWorld()); + final Location destiny = GeoEngine.getInstance().getValidLocation(curX, curY, curZ, x, y, z, getInstanceWorld()); x = destiny.getX(); y = destiny.getY(); z = destiny.getZ(); @@ -3443,7 +3441,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { if (isPlayer() && !_isFlying && !isInWater) diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/actor/Summon.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/actor/Summon.java index 87d65e23d3..ad18df7166 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/actor/Summon.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/actor/Summon.java @@ -106,7 +106,7 @@ public abstract class Summon extends Playable final int x = owner.getX(); final int y = owner.getY(); final int z = owner.getZ(); - final Location location = GeoEngine.getInstance().canMoveToTargetLoc(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, owner.getInstanceWorld()); + final Location location = GeoEngine.getInstance().getValidLocation(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, getInstanceWorld()); setXYZInvisible(location.getX(), location.getY(), location.getZ()); } diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index ba4c469cbe..4f5acd7e73 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -14275,8 +14275,8 @@ public class PlayerInstance extends Playable public boolean isInTimedHuntingZone(int zoneId, int locX, int locY) { - final int x = ((locX - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((locY - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((locX - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((locY - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; switch (zoneId) { diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java index abbe632775..8943842d96 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java @@ -1569,7 +1569,7 @@ public class ItemInstance extends WorldObject if (dropper != null) { final Instance instance = dropper.getInstanceWorld(); - final Location dropDest = GeoEngine.getInstance().canMoveToTargetLoc(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); + final Location dropDest = GeoEngine.getInstance().getValidLocation(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); x = dropDest.getX(); y = dropDest.getY(); z = dropDest.getZ(); diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java index b2be9fb5e2..0bc496e8cc 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java @@ -1179,7 +1179,7 @@ public class SkillCaster implements Runnable } } - final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().canMoveToTargetLoc(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); + final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().getValidLocation(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); creature.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); creature.broadcastPacket(new FlyToLocation(creature, destination, flyType, 0, 0, 333)); diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java index c62c821f44..30068134e3 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java @@ -114,7 +114,7 @@ public class ValidatePosition implements IClientIncomingPacket { if (player.isFalling(_z)) { - final int nearestZ = GeoEngine.getInstance().getHigherHeight(_x, _y, _z); + final int nearestZ = GeoEngine.getInstance().getHeight(_x, _y, _z); if (player.getZ() < nearestZ) { player.setXYZ(_x, _y, nearestZ); diff --git a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/util/GeoUtils.java index 4600a0fc53..19a979819e 100644 --- a/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_9.0_ReturnOfTheQueenAnt/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -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.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; @@ -30,21 +30,21 @@ public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getInstance().getWorldX(iter.x()); - final int wy = GeoEngine.getInstance().getWorldY(iter.y()); + final int wx = GeoEngine.getWorldX(iter.x()); + final int wy = GeoEngine.getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public final class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); - int wy = GeoEngine.getInstance().getWorldY(prevY); + int wx = GeoEngine.getWorldX(prevX); + int wy = GeoEngine.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.getInstance().getWorldX(curX); - wy = GeoEngine.getInstance().getWorldY(curY); + wx = GeoEngine.getWorldX(curX); + wy = GeoEngine.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().checkNearestNswe(x, y, z, nswe)) + if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0) { return Color.GREEN; } @@ -109,9 +109,8 @@ public final class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final GeoEngine ge = GeoEngine.getInstance(); - final int playerGx = ge.getGeoX(player.getX()); - final int playerGy = ge.getGeoY(player.getY()); + final int playerGx = GeoEngine.getGeoX(player.getX()); + final int playerGy = GeoEngine.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -135,32 +134,32 @@ public final class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = ge.getWorldX(gx); - final int y = ge.getWorldY(gy); - final int z = ge.getNearestZ(gx, gy, player.getZ()); + final int x = GeoEngine.getWorldX(gx); + final int y = GeoEngine.getWorldY(gy); + final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); + Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); 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, Cell.NSWE_EAST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); 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, Cell.NSWE_SOUTH); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); 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, Cell.NSWE_WEST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); 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); @@ -188,41 +187,41 @@ public final class GeoUtils { if (y > lastY) { - return Cell.NSWE_SOUTH_EAST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E; } else if (y < lastY) { - return Cell.NSWE_NORTH_EAST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E; } else { - return Cell.NSWE_EAST; + return GeoStructure.CELL_FLAG_E; } } else if (x < lastX) // west { if (y > lastY) { - return Cell.NSWE_SOUTH_WEST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W; } else if (y < lastY) { - return Cell.NSWE_NORTH_WEST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W; } else { - return Cell.NSWE_WEST; + return GeoStructure.CELL_FLAG_W; } } else // unchanged x { if (y > lastY) { - return Cell.NSWE_SOUTH; + return GeoStructure.CELL_FLAG_S; } else if (y < lastY) { - return Cell.NSWE_NORTH; + return GeoStructure.CELL_FLAG_N; } else { diff --git a/L2J_Mobius_C4_ScionsOfDestiny/dist/game/config/main/GeoEngine.ini b/L2J_Mobius_C4_ScionsOfDestiny/dist/game/config/main/GeoEngine.ini index 4adfbea760..6e4b5db075 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/dist/game/config/main/GeoEngine.ini +++ b/L2J_Mobius_C4_ScionsOfDestiny/dist/game/config/main/GeoEngine.ini @@ -6,37 +6,52 @@ # at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ 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 + # ================================================================= -# Path finding +# Pathfinding # ================================================================= # 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 +# an alternative path (e.g. walk around obstacle), default: True +PathFinding = True -# Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 -PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 +# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 -# Weight for nodes without obstacles far from walls -LowWeight = 0.5 +# Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 +MoveWeight = 10 +MoveWeightDiag = 14 -# Weight for nodes near walls -MediumWeight = 2 +# 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 -# Weight for nodes with obstacles -HighWeight = 3 +# 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. -# Default: LowWeight * sqrt(2) -DiagonalWeight = 0.707 +# Maximum number of generated nodes per one path-finding process, default 3500 +MaxIterations = 3500 + +# ================================================================= +# 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 # ================================================================= # Other # ================================================================= -# Correct player Z after falling through the ground. -CorrectPlayerZ = False - # When a character falls, he takes damages. # Default: True FallDamage = True diff --git a/L2J_Mobius_C4_ScionsOfDestiny/dist/game/data/geodata/L2D_GeoEngine.patch b/L2J_Mobius_C4_ScionsOfDestiny/dist/game/data/geodata/L2D_GeoEngine.patch deleted file mode 100644 index 473e18592e..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/dist/game/data/geodata/L2D_GeoEngine.patch +++ /dev/null @@ -1,5951 +0,0 @@ -Index: dist/game/GeoDataConverter.bat -=================================================================== ---- dist/game/GeoDataConverter.bat (nonexistent) -+++ dist/game/GeoDataConverter.bat (working copy) -@@ -0,0 +1,6 @@ -+@echo off -+title L2D geodata converter -+ -+java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter -+ -+pause -Index: dist/game/GeoDataConverter.sh -=================================================================== ---- dist/game/GeoDataConverter.sh (nonexistent) -+++ dist/game/GeoDataConverter.sh (working copy) -@@ -0,0 +1,4 @@ -+#! /bin/sh -+ -+java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 -+ -Index: dist/game/config/main/GeoEngine.ini -=================================================================== ---- dist/game/config/main/GeoEngine.ini (revision 8319) -+++ dist/game/config/main/GeoEngine.ini (working copy) -@@ -1,6 +1,10 @@ - # ================================================================= - # Geodata - # ================================================================= -+# Because of real-time performance we are using geodata files only in -+# diagonal L2D format now (using filename e.g. 22_16.l2d). -+# L2D geodata can be obtained by conversion of existing L2J or L2OFF geodata. -+# Launch "GeoDataConverter.bat/sh" and follow instructions to start the conversion. - - # 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/ -@@ -13,6 +17,16 @@ - CoordSynchronize = 2 - - # ================================================================= -+# Path checking -+# ================================================================= -+ -+# 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 finding - # ================================================================= - -@@ -23,19 +37,23 @@ - # Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - --# Weight for nodes without obstacles far from walls --LowWeight = 0.5 -+# Base path weight, when moving from one node to another on axis direction, default: 10 -+BaseWeight = 10 - --# Weight for nodes near walls --MediumWeight = 2 -+# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -+DiagonalWeight = 14 - --# Weight for nodes with obstacles --HighWeight = 3 -+# When movement flags of target node is blocked to any direction, multiply movement weight by this multiplier. -+# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 10 -+ObstacleMultiplier = 10 - --# Weight for diagonal movement. --# Default: LowWeight * sqrt(2) --DiagonalWeight = 0.707 -+# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 20 -+# For proper function must be higher than BaseWeight and/or DiagonalWeight. -+HeuristicWeight = 20 - -+# Maximum number of generated nodes per one path-finding process, default 3500 -+MaxIterations = 3500 -+ - # ================================================================= - # Other - # ================================================================= -Index: java/org/l2jmobius/Config.java -=================================================================== ---- java/org/l2jmobius/Config.java (revision 8388) -+++ java/org/l2jmobius/Config.java (working copy) -@@ -27,8 +27,6 @@ - import java.io.LineNumberReader; - import java.io.OutputStream; - import java.math.BigInteger; --import java.nio.file.Path; --import java.nio.file.Paths; - import java.util.ArrayList; - import java.util.HashMap; - import java.util.List; -@@ -930,14 +928,17 @@ - public static boolean LEAVE_BUFFS_ON_DIE; - public static boolean ALT_RAIDS_STATS_BONUS; - -- public static Path GEODATA_PATH; -+ public static String GEODATA_PATH; -+ public static int COORD_SYNCHRONIZE; -+ public static int PART_OF_CHARACTER_HEIGHT; -+ public static int MAX_OBSTACLE_HEIGHT; - public static boolean PATHFINDING; - public static String PATHFIND_BUFFERS; -- public static float LOW_WEIGHT; -- public static float MEDIUM_WEIGHT; -- public static float HIGH_WEIGHT; -- public static float DIAGONAL_WEIGHT; -- public static int COORD_SYNCHRONIZE; -+ public static int BASE_WEIGHT; -+ public static int DIAGONAL_WEIGHT; -+ public static int HEURISTIC_WEIGHT; -+ public static int OBSTACLE_MULTIPLIER; -+ public static int MAX_ITERATIONS; - public static boolean CORRECT_PLAYER_Z; - public static boolean FALL_DAMAGE; - public static boolean ALLOW_WATER; -@@ -2495,14 +2496,17 @@ - public static void loadgeodataConfig() - { - final PropertiesParser geoengineConfig = new PropertiesParser(GEOENGINE_CONFIG_FILE); -- GEODATA_PATH = Paths.get(geoengineConfig.getString("GeoDataPath", "./data/geodata")); -+ GEODATA_PATH = geoengineConfig.getString("GeoDataPath", "./data/geodata/"); -+ COORD_SYNCHRONIZE = geoengineConfig.getInt("CoordSynchronize", -1); -+ PART_OF_CHARACTER_HEIGHT = geoengineConfig.getInt("PartOfCharacterHeight", 75); -+ MAX_OBSTACLE_HEIGHT = geoengineConfig.getInt("MaxObstacleHeight", 32); - PATHFINDING = geoengineConfig.getBoolean("PathFinding", true); - 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); -- DIAGONAL_WEIGHT = geoengineConfig.getFloat("DiagonalWeight", 0.707f); -- COORD_SYNCHRONIZE = geoengineConfig.getInt("CoordSynchronize", -1); -+ BASE_WEIGHT = geoengineConfig.getInt("BaseWeight", 10); -+ DIAGONAL_WEIGHT = geoengineConfig.getInt("DiagonalWeight", 14); -+ OBSTACLE_MULTIPLIER = geoengineConfig.getInt("ObstacleMultiplier", 10); -+ HEURISTIC_WEIGHT = geoengineConfig.getInt("HeuristicWeight", 20); -+ MAX_ITERATIONS = geoengineConfig.getInt("MaxIterations", 3500); - CORRECT_PLAYER_Z = geoengineConfig.getBoolean("CorrectPlayerZ", false); - FALL_DAMAGE = geoengineConfig.getBoolean("FallDamage", false); - ALLOW_WATER = geoengineConfig.getBoolean("AllowWater", false); -Index: java/org/l2jmobius/gameserver/geoengine/GeoEngine.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (revision 8352) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (working copy) -@@ -16,98 +16,89 @@ - */ - package org.l2jmobius.gameserver.geoengine; - --import java.io.IOException; -+import java.io.File; - import java.io.RandomAccessFile; - import java.nio.ByteOrder; --import java.nio.channels.FileChannel.MapMode; --import java.nio.file.Files; --import java.nio.file.Path; --import java.util.concurrent.atomic.AtomicReferenceArray; --import java.util.logging.Level; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.List; - import java.util.logging.Logger; - - import org.l2jmobius.Config; - import org.l2jmobius.gameserver.data.xml.DoorData; - import org.l2jmobius.gameserver.data.xml.FenceData; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; --import org.l2jmobius.gameserver.geoengine.geodata.IRegion; --import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; --import org.l2jmobius.gameserver.geoengine.geodata.Region; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.World; - import org.l2jmobius.gameserver.model.WorldObject; --import org.l2jmobius.gameserver.util.GeoUtils; --import org.l2jmobius.gameserver.util.LinePointIterator; --import org.l2jmobius.gameserver.util.LinePointIterator3D; -+import org.l2jmobius.gameserver.model.actor.Creature; -+import org.l2jmobius.gameserver.util.MathUtil; - - /** -- * @author -Nemesiss-, HorridoJoho -+ * @author Hasha - */ - public class GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); -+ protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - -- private static final int WORLD_MIN_X = -655360; -- private static final int WORLD_MIN_Y = -589824; -- private static final int WORLD_MIN_Z = -16384; -+ private final ABlock[][] _blocks; -+ private final BlockNull _nullBlock; - -- /** 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; -- -- /** The regions array */ -- private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); -- -- private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; -- private static final int ELEVATED_SEE_OVER_DISTANCE = 2; -- private static final int MAX_SEE_OVER_HEIGHT = 48; -- -- protected GeoEngine() -+ /** -+ * GeoEngine constructor. Loads all geodata files of chosen geodata format. -+ */ -+ public GeoEngine() - { - LOGGER.info("GeoEngine: Initializing..."); -- for (int i = 0; i < _regions.length(); i++) -- { -- _regions.set(i, NullRegion.INSTANCE); -- } - -+ // initialize block container -+ _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; -+ -+ // load null block -+ _nullBlock = new BlockNull(); -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files according to geoengine config setup - int loaded = 0; -- try -+ long fileSize = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { -- for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { -- for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) -+ final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); -+ if (f.exists() && !f.isDirectory()) - { -- final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); -- if (Files.exists(geoFilePath)) -+ // region file is load-able, try to load it -+ if (loadGeoBlocks(rx, ry)) - { -- try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) -- { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -- loaded++; -- } -+ loaded++; -+ fileSize += f.length(); - } - } -+ else -+ { -+ // region file is not load-able, load null blocks -+ loadNullBlocks(rx, ry); -+ } - } - } -- catch (Exception e) -+ -+ LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -+ if (loaded > 0) - { -- LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); -- System.exit(1); -+ LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); - } - -- LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -- -- // Avoid wrong configs when no files are loaded. -+ // avoid wrong configs when no files are loaded - if (loaded == 0) - { - if (Config.PATHFINDING) -@@ -121,619 +112,820 @@ - LOGGER.info("GeoEngine: Forcing CoordSynchronize setting to -1."); - } - } -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); - } - - /** -- * @param geoX -- * @param geoY -- * @return the region -+ * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. -+ * @return boolean : True, when geodata file was loaded without problem. - */ -- private IRegion getRegion(int geoX, int geoY) -+ private final boolean loadGeoBlocks(int regionX, int regionY) - { -- final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); -- if ((region < 0) || (region >= _regions.length())) -+ final String filename = String.format(GeoFormat.L2D.getFilename(), regionX, regionY); -+ -+ // standard load -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) - { -- return null; -+ // initialize file buffer -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, GeoFormat.L2D); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ } -+ -+ // check data consistency -+ if (buffer.remaining() > 0) -+ { -+ LOGGER.warning("GeoEngine: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ } -+ -+ // loading was successful -+ return true; - } -- return _regions.get(region); -- } -- -- /** -- * @param filePath -- * @param regionX -- * @param regionY -- * @throws IOException -- */ -- 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")) -+ catch (Exception e) - { -- _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -+ // an error occured while loading, load null blocks -+ LOGGER.warning("GeoEngine: Error while loading " + filename + " region file."); -+ LOGGER.warning(e.getMessage()); -+ -+ // replace whole region file with null blocks -+ loadNullBlocks(regionX, regionY); -+ -+ // loading was not successful -+ return false; - } - } - - /** -- * @param regionX -- * @param regionY -+ * Loads null blocks. Used when no region file is detected or an error occurs during loading. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. - */ -- public void unloadRegion(int regionX, int regionY) -+ private final void loadNullBlocks(int regionX, int regionY) - { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); -- } -- -- /** -- * @param geoX -- * @param geoY -- * @return if geodata exist -- */ -- public boolean hasGeoPos(int geoX, int geoY) -- { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // load all null blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { -- return false; -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[blockX + ix][blockY + iy] = _nullBlock; -+ } - } -- return region.hasGeo(); - } - -+ // GEODATA - GENERAL -+ - /** -- * Checks the specified position for available geodata. -- * @param x the world x -- * @param y the world y -- * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise -+ * Converts world X to geodata X. -+ * @param worldX -+ * @return int : Geo X - */ -- public boolean hasGeo(int x, int y) -+ public static int getGeoX(int worldX) - { -- return hasGeoPos(getGeoX(x), getGeoY(y)); -+ return (MathUtil.limit(worldX, World.MAP_MIN_X, World.MAP_MAX_X) - World.MAP_MIN_X) >> 4; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe check -+ * Converts world Y to geodata Y. -+ * @param worldY -+ * @return int : Geo Y - */ -- public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) -+ public static int getGeoY(int worldY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return true; -- } -- return region.checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(worldY, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.MAP_MIN_Y) >> 4; - } - - /** -+ * Converts geodata X to world X. - * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe anti-corner cut -+ * @return int : World X - */ -- public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) -+ public static int getWorldX(int geoX) - { -- boolean can = true; -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- return can && checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(geoX, 0, GeoStructure.GEO_CELLS_X) << 4) + World.MAP_MIN_X + 8; - } - - /** -- * @param geoX -+ * Converts geodata Y to world Y. - * @param geoY -- * @param worldZ -- * @return the nearest Z value -+ * @return int : World Y - */ -- public int getNearestZ(int geoX, int geoY, int worldZ) -+ public static int getWorldY(int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return worldZ; -- } -- return region.getNearestZ(geoX, geoY, worldZ); -+ return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next lower Z value -+ * Returns block of geodata on given coordinates. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return {@link ABlock} : Block of geodata. - */ -- public int getNextLowerZ(int geoX, int geoY, int worldZ) -+ private final ABlock getBlock(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final int x = geoX / GeoStructure.BLOCK_CELLS_X; -+ final int y = geoY / GeoStructure.BLOCK_CELLS_Y; -+ -+ // if x or y is out of array return null -+ if ((x > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) - { -- return worldZ; -+ return _blocks[x][y]; - } -- return region.getNextLowerZ(geoX, geoY, worldZ); -+ return null; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next higher Z value -+ * Check if geo coordinates has geo. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return boolean : True, if given geo coordinates have geodata - */ -- public int getNextHigherZ(int geoX, int geoY, int worldZ) -+ public boolean hasGeoPos(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final ABlock block = getBlock(geoX, geoY); -+ if (block == null) // null block check - { -- return worldZ; -+ // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) -+ // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); -+ return false; - } -- return region.getNextHigherZ(geoX, geoY, worldZ); -+ return block.hasGeoPos(); - } - - /** -- * Gets the Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the height of cell, which is closest to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, closest to given coordinates. - */ -- public int getHeight(int x, int y, int z) -+ public short getHeightNearest(int geoX, int geoY, int worldZ) - { -- return getNearestZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getHeightNearest(geoX, geoY, worldZ) : (short) worldZ; - } - - /** -- * Gets the next lower Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the NSWE flag byte of cell, which is closes to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. - */ -- public int getLowerHeight(int x, int y, int z) -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) - { -- return getNextLowerZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getNsweNearest(geoX, geoY, worldZ) : (byte) 0xFF; - } - - /** -- * Gets the next higher Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Check if world coordinates has geo. -+ * @param worldX : World X -+ * @param worldY : World Y -+ * @return boolean : True, if given world coordinates have geodata - */ -- public int getHigherHeight(int x, int y, int z) -+ public boolean hasGeo(int worldX, int worldY) - { -- return getNextHigherZ(getGeoX(x), getGeoY(y), z); -+ return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); - } - - /** -- * @param worldX -- * @return the geo X -+ * Returns closest Z coordinate according to geodata. -+ * @param worldX : world x -+ * @param worldY : world y -+ * @param worldZ : world z -+ * @return short : nearest Z coordinates according to geodata - */ -- public int getGeoX(int worldX) -+ public short getHeight(int worldX, int worldY, int worldZ) - { -- return (worldX - WORLD_MIN_X) / 16; -+ return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - -- /** -- * @param worldY -- * @return the geo Y -- */ -- public int getGeoY(int worldY) -- { -- return (worldY - WORLD_MIN_Y) / 16; -- } -+ // PATHFINDING - - /** -- * @param worldZ -- * @return the geo Z -+ * Check line of sight from {@link WorldObject} to {@link WorldObject}. -+ * @param origin : The origin object. -+ * @param target : The target object. -+ * @return {@code boolean} : True if origin can see target - */ -- public int getGeoZ(int worldZ) -+ public boolean canSeeTarget(WorldObject origin, WorldObject target) - { -- return (worldZ - WORLD_MIN_Z) / 16; -- } -- -- /** -- * @param geoX -- * @return the world X -- */ -- public int getWorldX(int geoX) -- { -- return (geoX * 16) + WORLD_MIN_X + 8; -- } -- -- /** -- * @param geoY -- * @return the world Y -- */ -- public int getWorldY(int geoY) -- { -- return (geoY * 16) + WORLD_MIN_Y + 8; -- } -- -- /** -- * @param geoZ -- * @return the world Z -- */ -- public int getWorldZ(int geoZ) -- { -- return (geoZ * 16) + WORLD_MIN_Z + 8; -- } -- -- /** -- * Can see target. Doors as target always return true. Checks doors between. -- * @param cha the character -- * @param target the target -- * @return {@code true} if the character can see the target (LOS), {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, WorldObject target) -- { -- if (target.isDoor()) -+ if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) - { -- // Can always see doors. - return true; - } -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), target.getX(), target.getY(), target.getZ(), target.getInstanceId()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param cha the character -- * @param worldPosition the world position -- * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, Location worldPosition) -- { -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instanceId -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @param tInstanceId the target's instanceId -- * @return -- */ -- public boolean canSeeTarget(int x, int y, int z, int instanceId, int tx, int ty, int tz, int tInstanceId) -- { -- return (instanceId != tInstanceId) ? false : canSeeTarget(x, y, z, instanceId, tx, ty, tz); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instanceId -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -- */ -- public boolean canSeeTarget(int x, int y, int z, int instanceId, int tx, int ty, int tz) -- { -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz)) -+ -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = target.getX(); -+ final int ty = target.getY(); -+ final int tz = target.getZ(); -+ -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz)) - { - return false; - } -- return canSeeTarget(x, y, z, tx, ty, tz); -- } -- -- /** -- * @param prevX -- * @param prevY -- * @param prevGeoZ -- * @param curX -- * @param curY -- * @param nswe -- * @return the LOS Z value -- */ -- private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) -- { -- if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz)) - { -- throw new RuntimeException("Multiple directions!"); -+ return false; - } - -- if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- return getNearestZ(curX, curY, prevGeoZ); -+ return true; - } - -- return getNextHigherZ(curX, curY, prevGeoZ); -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) -+ { -+ return true; -+ } -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) -+ { -+ return goz == gtz; -+ } -+ -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) -+ { -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight() * 2; -+ } -+ -+ double theight = 0; -+ if (target.isCreature()) -+ { -+ theight = ((Creature) target).getTemplate().getCollisionHeight() * 2; -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceId()); - } - - /** -- * Can see target. Does not check doors between. -- * @param xValue the x coordinate -- * @param yValue the y coordinate -- * @param zValue the z coordinate -- * @param txValue the target's x coordinate -- * @param tyValue the target's y coordinate -- * @param tzValue the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -+ * Check line of sight from {@link WorldObject} to {@link Location}. -+ * @param origin : The origin object. -+ * @param position : The target position. -+ * @return {@code boolean} : True if object can see position - */ -- public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) -+ public boolean canSeeTarget(WorldObject origin, Location position) - { -- int x = xValue; -- int y = yValue; -- int tx = txValue; -- int ty = tyValue; -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = position.getX(); -+ final int ty = position.getY(); -+ final int tz = position.getZ(); - -- int geoX = getGeoX(x); -- int geoY = getGeoY(y); -- int tGeoX = getGeoX(tx); -- int tGeoY = getGeoY(ty); -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz)) -+ { -+ return false; -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz)) -+ { -+ return false; -+ } - -- int z = getNearestZ(geoX, geoY, zValue); -- int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- // Fastpath. -- if ((geoX == tGeoX) && (geoY == tGeoY)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- if (hasGeoPos(tGeoX, tGeoY)) -- { -- return z == tz; -- } - return true; - } - -- if (tz > z) -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) - { -- int tmp = tx; -- tx = x; -- x = tmp; -- -- tmp = ty; -- ty = y; -- y = tmp; -- -- tmp = tz; -- tz = z; -- z = tmp; -- -- tmp = tGeoX; -- tGeoX = geoX; -- geoX = tmp; -- -- tmp = tGeoY; -- tGeoY = geoY; -- geoY = tmp; -+ return goz == gtz; - } - -- final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); -- // First point is guaranteed to be available, skip it, we can always see our own position. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- final int prevZ = pointIter.z(); -- int prevGeoZ = prevZ; -- int ptIndex = 0; -- while (pointIter.next()) -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight(); -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceId()); -+ } -+ -+ /** -+ * Simple check for origin to target visibility. -+ * @param goxValue : origin X geodata coordinate -+ * @param goyValue : origin Y geodata coordinate -+ * @param gozValue : origin Z geodata coordinate -+ * @param oheight : origin height (if instance of {@link Character}) -+ * @param gtxValue : target X geodata coordinate -+ * @param gtyValue : target Y geodata coordinate -+ * @param gtzValue : target Z geodata coordinate -+ * @param theight : target height (if instance of {@link Character}) -+ * @param instanceId -+ * @return {@code boolean} : True, when target can be seen. -+ */ -+ private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, int instanceId) -+ { -+ int goz = gozValue; -+ int gtz = gtzValue; -+ int gox = goxValue; -+ int goy = goyValue; -+ int gtx = gtxValue; -+ int gty = gtyValue; -+ -+ // get line of sight Z coordinates -+ double losoz = goz + ((oheight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ double lostz = gtz + ((theight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ -+ // get X delta and signum -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirox = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; -+ final byte dirtx = sx > 0 ? GeoStructure.CELL_FLAG_W : GeoStructure.CELL_FLAG_E; -+ -+ // get Y delta and signum -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte diroy = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ final byte dirty = sy > 0 ? GeoStructure.CELL_FLAG_N : GeoStructure.CELL_FLAG_S; -+ -+ // get Z delta -+ final int dm = Math.max(dx, dy); -+ final double dz = (lostz - losoz) / dm; -+ -+ // get direction flag for diagonal movement -+ final byte diroxy = getDirXY(dirox, diroy); -+ final byte dirtxy = getDirXY(dirtx, dirty); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte diro; -+ byte dirt; -+ -+ // initialize node values -+ int nox = gox; -+ int noy = goy; -+ int ntx = gtx; -+ int nty = gty; -+ byte nsweo = getNsweNearest(gox, goy, goz); -+ byte nswet = getNsweNearest(gtx, gty, gtz); -+ -+ // loop -+ ABlock block; -+ int index; -+ for (int i = 0; i < ((dm + 1) / 2); i++) -+ { -+ // reset direction flag -+ diro = 0; -+ dirt = 0; - -- if ((curX == prevX) && (curY == prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- continue; -+ // calculate next point XY coordinates -+ d -= dy; -+ d += dx; -+ nox += sx; -+ ntx -= sx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroxy; -+ dirt |= dirtxy; - } -+ else if (e2 > -dy) -+ { -+ // calculate next point X coordinate -+ d -= dy; -+ nox += sx; -+ ntx -= sx; -+ diro |= dirox; -+ dirt |= dirtx; -+ } -+ else if (e2 < dx) -+ { -+ // calculate next point Y coordinate -+ d += dx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroy; -+ dirt |= dirty; -+ } - -- final int beeCurZ = pointIter.z(); -- int curGeoZ = prevGeoZ; -- -- // Check if the position has geodata. -- if (hasGeoPos(curX, curY)) - { -- final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); -- curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); -- int maxHeight; -- if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) -+ // get block of the next cell -+ block = getBlock(nox, noy); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nsweo & diro) == 0) - { -- maxHeight = z + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); - } - else - { -- maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); - } - -- boolean canSeeThrough = false; -- if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) -+ // layer does not exist, return -+ if (index == -1) - { -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else -- { -- canSeeThrough = true; -- } -+ return false; - } - -- if (!canSeeThrough) -+ // get layer and next line of sight Z coordinate -+ goz = block.getHeight(index); -+ losoz += dz; -+ -+ // perform line of sight check, return when fails -+ if ((goz - losoz) > Config.MAX_OBSTACLE_HEIGHT) - { - return false; - } -+ -+ // get layer nswe -+ nsweo = block.getNswe(index); - } -+ { -+ // get block of the next cell -+ block = getBlock(ntx, nty); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nswet & dirt) == 0) -+ { -+ index = block.getIndexAbove(ntx, nty, gtz - GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ else -+ { -+ index = block.getIndexBelow(ntx, nty, gtz + GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ -+ // layer does not exist, return -+ if (index == -1) -+ { -+ return false; -+ } -+ -+ // get layer and next line of sight Z coordinate -+ gtz = block.getHeight(index); -+ lostz -= dz; -+ -+ // perform line of sight check, return when fails -+ if ((gtz - lostz) > Config.MAX_OBSTACLE_HEIGHT) -+ { -+ return false; -+ } -+ -+ // get layer nswe -+ nswet = block.getNswe(index); -+ } - -- prevX = curX; -- prevY = curY; -- prevGeoZ = curGeoZ; -- ++ptIndex; -+ // update coords -+ gox = nox; -+ goy = noy; -+ gtx = ntx; -+ gty = nty; - } - -- return true; -+ // when iteration is completed, compare final Z coordinates -+ return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); - } - - /** -- * Move check. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param zValue the z coordinate -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tzValue the target's z coordinate -- * @param instanceId the instance id -- * @return the last Location (x,y,z) where player can walk - just before wall -+ * Check movement from coordinates to coordinates. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instanceId -+ * @return {code boolean} : True if target coordinates are reachable from origin coordinates - */ -- public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, int instanceId) -+ public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) - { -- final int geoX = getGeoX(x); -- final int geoY = getGeoY(y); -- final int z = getNearestZ(geoX, geoY, zValue); -- final int tGeoX = getGeoX(tx); -- final int tGeoY = getGeoY(ty); -- final int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } -- if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz)) -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = z; -+ // perform geodata check -+ final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instanceId); -+ return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); -+ } -+ -+ /** -+ * Check movement from origin to target. Returns last available point in the checked path. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instanceId -+ * @return {@link Location} : Last point where object can walk (just before wall) -+ */ -+ public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) -+ { -+ // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz)) -+ { -+ return new Location(ox, oy, oz); -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz)) -+ { -+ return new Location(ox, oy, oz); -+ } - -- while (pointIter.next()) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -- -- if (hasGeoPos(prevX, prevY)) -- { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -- { -- // Can't move, return previous location. -- return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); -- } -- } -- -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return new Location(tx, ty, tz); - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != tz)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- // Different floors, return start location. -- return new Location(x, y, z); -+ return new Location(tx, ty, tz); - } - -- return new Location(tx, ty, tz); -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) -+ { -+ return new Location(tx, ty, tz); -+ } -+ -+ // perform geodata check -+ return checkMove(gox, goy, goz, gtx, gty, gtz, instanceId); - } - - /** -- * Checks if its possible to move from one location to another. -- * @param fromX the X coordinate to start checking from -- * @param fromY the Y coordinate to start checking from -- * @param fromZvalue the Z coordinate to start checking from -- * @param toX the X coordinate to end checking at -- * @param toY the Y coordinate to end checking at -- * @param toZvalue the Z coordinate to end checking at -- * @param instanceId the instance -- * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise -+ * With this method you can check if a position is visible or can be reached by beeline movement.
-+ * Target X and Y reachable and Z is on same floor: -+ *
    -+ *
  • Location of the target with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y reachable but Z is on another floor: -+ *
    -+ *
  • Location of the origin with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y not reachable: -+ *
    -+ *
  • Last accessible location in destination to target.
  • -+ *
-+ * @param gox : origin X geodata coordinate -+ * @param goy : origin Y geodata coordinate -+ * @param goz : origin Z geodata coordinate -+ * @param gtx : target X geodata coordinate -+ * @param gty : target Y geodata coordinate -+ * @param gtz : target Z geodata coordinate -+ * @param instanceId -+ * @return {@link GeoLocation} : The last allowed point of movement. - */ -- public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, int instanceId) -+ protected final GeoLocation checkMove(int gox, int goy, int goz, int gtx, int gty, int gtz, int instanceId) - { -- final int geoX = getGeoX(fromX); -- final int geoY = getGeoY(fromY); -- final int fromZ = getNearestZ(geoX, geoY, fromZvalue); -- final int tGeoX = getGeoX(toX); -- final int tGeoY = getGeoY(toY); -- final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); -- -- if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ)) -+ if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } -- if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ)) -+ if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = fromZ; -+ // get X delta, signum and direction flag -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirX = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; - -- while (pointIter.next()) -+ // get Y delta, signum and direction flag -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte dirY = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ -+ // get direction flag for diagonal movement -+ final byte dirXY = getDirXY(dirX, dirY); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte direction; -+ -+ // load pointer coordinates -+ int gpx = gox; -+ int gpy = goy; -+ int gpz = goz; -+ -+ // load next pointer -+ int nx = gpx; -+ int ny = gpy; -+ -+ // loop -+ int count = 0; -+ while (count++ < Config.MAX_ITERATIONS) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -+ direction = 0; - -- if (hasGeoPos(prevX, prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -+ d -= dy; -+ d += dx; -+ nx += sx; -+ ny += sy; -+ direction |= dirXY; -+ } -+ else if (e2 > -dy) -+ { -+ d -= dy; -+ nx += sx; -+ direction |= dirX; -+ } -+ else if (e2 < dx) -+ { -+ d += dx; -+ ny += sy; -+ direction |= dirY; -+ } -+ -+ // obstacle found, return -+ if ((getNsweNearest(gpx, gpy, gpz) & direction) == 0) -+ { -+ return new GeoLocation(gpx, gpy, gpz); -+ } -+ -+ // update pointer coordinates -+ gpx = nx; -+ gpy = ny; -+ gpz = getHeightNearest(nx, ny, gpz); -+ -+ // target coordinates reached -+ if ((gpx == gtx) && (gpy == gty)) -+ { -+ if (gpz == gtz) - { -- return false; -+ // path found, Z coordinates are okay, return target point -+ return new GeoLocation(gtx, gty, gtz); - } -+ -+ // path found, Z coordinates are not okay, return last good point -+ return new GeoLocation(gpx, gpy, gpz); - } -+ } -+ -+ return new GeoLocation(gox, goy, goz); -+ } -+ -+ /** -+ * Returns diagonal NSWE flag format of combined two NSWE flags. -+ * @param dirX : X direction NSWE flag -+ * @param dirY : Y direction NSWE flag -+ * @return byte : NSWE flag of combined direction -+ */ -+ private static byte getDirXY(byte dirX, byte dirY) -+ { -+ // check axis directions -+ if (dirY == GeoStructure.CELL_FLAG_N) -+ { -+ if (dirX == GeoStructure.CELL_FLAG_W) -+ { -+ return GeoStructure.CELL_FLAG_NW; -+ } - -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return GeoStructure.CELL_FLAG_NE; - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) -+ if (dirX == GeoStructure.CELL_FLAG_W) - { -- // Different floors. -- return false; -+ return GeoStructure.CELL_FLAG_SW; - } - -- return true; -+ return GeoStructure.CELL_FLAG_SE; - } - -+ /** -+ * Returns the list of location objects as a result of complete path calculation. -+ * @param ox : origin x -+ * @param oy : origin y -+ * @param oz : origin z -+ * @param tx : target x -+ * @param ty : target y -+ * @param tz : target z -+ * @param instanceId -+ * @return {@code List} : complete path from nodes -+ */ -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) -+ { -+ return null; -+ } -+ -+ /** -+ * Returns the instance of the {@link GeoEngine}. -+ * @return {@link GeoEngine} : The instance. -+ */ - public static GeoEngine getInstance() - { - return SingletonHolder.INSTANCE; -@@ -741,6 +933,6 @@ - - private static class SingletonHolder - { -- protected static final GeoEngine INSTANCE = new GeoEngine(); -+ protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); - } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (working copy) -@@ -20,87 +20,83 @@ - import java.util.LinkedList; - import java.util.List; - import java.util.ListIterator; --import java.util.logging.Level; --import java.util.logging.Logger; - - import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; --import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; --import org.l2jmobius.gameserver.model.World; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -+import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -+import org.l2jmobius.gameserver.model.Location; - - /** -- * @author -Nemesiss- -+ * @author Hasha - */ --public class GeoEnginePathfinding -+final class GeoEnginePathfinding extends GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); -+ // pre-allocated buffers -+ private final BufferHolder[] _buffers; - -- private BufferInfo[] _buffers; -- - protected GeoEnginePathfinding() - { -- try -+ super(); -+ -+ final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ _buffers = new BufferHolder[array.length]; -+ int count = 0; -+ for (int i = 0; i < array.length; i++) - { -- final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ final String buf = array[i]; -+ final String[] args = buf.split("x"); - -- _buffers = new BufferInfo[array.length]; -- -- String buf; -- String[] args; -- for (int i = 0; i < array.length; i++) -+ try - { -- buf = array[i]; -- args = buf.split("x"); -- if (args.length != 2) -- { -- throw new Exception("Invalid buffer definition: " + buf); -- } -- -- _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); -+ final int size = Integer.parseInt(args[1]); -+ count += size; -+ _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); - } -+ catch (Exception e) -+ { -+ LOGGER.warning("GeoEnginePathfinding: Can not load buffer setting: " + buf); -+ } - } -- catch (Exception e) -- { -- LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); -- throw new Error("CellPathFinding: load aborted"); -- } -+ -+ LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); - } - -- public boolean pathNodesExist(short regionoffset) -+ @Override -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) - { -- return false; -- } -- -- public List findPath(int x, int y, int z, int tx, int ty, int tz, int instanceId) -- { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -- if (!GeoEngine.getInstance().hasGeo(x, y)) -+ // get origin and check existing geo coords -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { - 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)) -+ -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coords -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { - 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)))); -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // Prepare buffer for pathfinding calculations -+ final NodeBuffer buffer = getBuffer(64 + (2 * Math.max(Math.abs(gox - gtx), Math.abs(goy - gty)))); - if (buffer == null) - { - return null; - } - -- List path = null; -+ // find path -+ List path = null; - try - { -- final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); -- -+ final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); - if (result == null) - { - return null; -@@ -124,33 +120,45 @@ - return path; - } - -- int currentX, currentY, currentZ; -- ListIterator middlePoint; -+ // get path list iterator -+ final ListIterator point = path.listIterator(); - -- middlePoint = path.listIterator(); -- currentX = x; -- currentY = y; -- currentZ = z; -+ // get node A (origin) -+ int nodeAx = gox; -+ int nodeAy = goy; -+ short nodeAz = goz; - -- while (middlePoint.hasNext()) -+ // get node B -+ GeoLocation nodeB = (GeoLocation) point.next(); -+ -+ // iterate thought the path to optimize it -+ int count = 0; -+ while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) - { -- final AbstractNodeLoc locMiddle = middlePoint.next(); -- if (!middlePoint.hasNext()) -- { -- break; -- } -+ // get node C -+ final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - -- final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); -- if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instanceId)) -+ // check movement from node A to node C -+ final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instanceId); -+ if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) - { -- middlePoint.remove(); -+ // can move from node A to node C -+ -+ // remove node B -+ point.remove(); - } - else - { -- currentX = locMiddle.getX(); -- currentY = locMiddle.getY(); -- currentZ = locMiddle.getZ(); -+ // can not move from node A to node C -+ -+ // set node A (node B is part of path, update A coordinates) -+ nodeAx = nodeB.getGeoX(); -+ nodeAy = nodeB.getGeoY(); -+ nodeAz = (short) nodeB.getZ(); - } -+ -+ // set node B -+ nodeB = (GeoLocation) point.next(); - } - - return path; -@@ -157,144 +165,102 @@ - } - - /** -- * Convert geodata position to pathnode position -- * @param geo_pos -- * @return pathnode position -+ * Create list of node locations as result of calculated buffer node tree. -+ * @param node : the entry point -+ * @return List : list of node location - */ -- public short getNodePos(int geo_pos) -+ private static List constructPath(Node node) - { -- return (short) (geo_pos >> 3); // OK? -- } -- -- /** -- * Convert node position to pathnode block position -- * @param node_pos -- * @return pathnode block position (0...255) -- */ -- public short getNodeBlock(int node_pos) -- { -- return (short) (node_pos % 256); -- } -- -- public byte getRegionX(int node_pos) -- { -- return (byte) ((node_pos >> 8) + World.TILE_X_MIN); -- } -- -- public byte getRegionY(int node_pos) -- { -- return (byte) ((node_pos >> 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 node_x rx -- * @return -- */ -- public int calculateWorldX(short node_x) -- { -- return World.MAP_MIN_X + (node_x * 128) + 48; -- } -- -- /** -- * Convert pathnode y to World y position -- * @param node_y -- * @return -- */ -- public int calculateWorldY(short node_y) -- { -- return World.MAP_MIN_Y + (node_y * 128) + 48; -- } -- -- private List constructPath(AbstractNode nodeValue) -- { -- final LinkedList path = new LinkedList<>(); -- int previousDirectionX = Integer.MIN_VALUE; -- int previousDirectionY = Integer.MIN_VALUE; -- int directionX, directionY; -+ // create empty list -+ final LinkedList list = new LinkedList<>(); - -- AbstractNode node = nodeValue; -- while (node.getParent() != null) -+ // set direction X/Y -+ int dx = 0; -+ int dy = 0; -+ -+ // get target parent -+ Node target = node; -+ Node parent = target.getParent(); -+ -+ // while parent exists -+ int count = 0; -+ while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { -- directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); -- directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); -+ // get parent <> target direction X/Y -+ final int nx = parent.getLoc().getGeoX() - target.getLoc().getGeoX(); -+ final int ny = parent.getLoc().getGeoY() - target.getLoc().getGeoY(); - -- // only add a new route point if moving direction changes -- if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) -+ // direction has changed? -+ if ((dx != nx) || (dy != ny)) - { -- previousDirectionX = directionX; -- previousDirectionY = directionY; -+ // add node to the beginning of the list -+ list.addFirst(target.getLoc()); - -- path.addFirst(node.getLoc()); -- node.setLoc(null); -+ // update direction X/Y -+ dx = nx; -+ dy = ny; - } - -- node = node.getParent(); -+ // move to next node, set target and get its parent -+ target = parent; -+ parent = target.getParent(); - } - -- return path; -+ // return list -+ return list; - } - -- private CellNodeBuffer alloc(int size) -+ /** -+ * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer. -+ * @param size : pre-calculated minimal required size -+ * @return NodeBuffer : buffer -+ */ -+ private final NodeBuffer getBuffer(int size) - { -- CellNodeBuffer current = null; -- for (BufferInfo i : _buffers) -+ NodeBuffer current = null; -+ for (BufferHolder holder : _buffers) - { -- if (i.mapSize >= size) -+ // Find proper size of buffer -+ if (holder._size < size) - { -- for (CellNodeBuffer buf : i.buffer) -+ continue; -+ } -+ -+ // Find unlocked NodeBuffer -+ for (NodeBuffer buffer : holder._buffer) -+ { -+ if (!buffer.isLocked()) - { -- if (buf.lock()) -- { -- current = buf; -- break; -- } -+ continue; - } -- if (current != null) -- { -- break; -- } - -- // not found, allocate temporary buffer -- current = new CellNodeBuffer(i.mapSize); -- current.lock(); -- if (i.buffer.size() < i.count) -- { -- i.buffer.add(current); -- break; -- } -+ return buffer; - } -+ -+ // NodeBuffer not found, allocate temporary buffer -+ current = new NodeBuffer(holder._size); -+ current.isLocked(); - } - - return current; - } - -- private static final class BufferInfo -+ /** -+ * NodeBuffer container with specified size and count of separate buffers. -+ */ -+ private static final class BufferHolder - { -- final int mapSize; -- final int count; -- ArrayList buffer; -+ final int _size; -+ List _buffer; - -- public BufferInfo(int size, int cnt) -+ public BufferHolder(int size, int count) - { -- mapSize = size; -- count = cnt; -- buffer = new ArrayList<>(count); -+ _size = size; -+ _buffer = new ArrayList<>(count); -+ for (int i = 0; i < count; i++) -+ { -+ _buffer.add(new NodeBuffer(size)); -+ } - } - } -- -- public static GeoEnginePathfinding getInstance() -- { -- return SingletonHolder.INSTANCE; -- } -- -- private static class SingletonHolder -- { -- protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); -- } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (working copy) -@@ -0,0 +1,197 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @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 height of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getHeightNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first above given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, above given coordinates. -+ */ -+ public abstract short getHeightAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first below given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is closest to given coordinates. -+ * @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 the NSWE flag byte of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getNsweNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first above given coordinates. -+ * @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 getNsweAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first below given coordinates. -+ * @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 getNsweBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is closes layer to given coordinates. -+ * @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. -+ * @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 above given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is first layer below given coordinates. -+ * @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 index to data of the cell, which is first layer below given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexBelowOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeight(int index); -+ -+ /** -+ * Returns the height of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightOriginal(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNswe(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNsweOriginal(int index); -+ -+ /** -+ * Sets the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @param nswe : New NSWE flag byte. -+ */ -+ public abstract void setNswe(int index, byte nswe); -+ -+ /** -+ * Saves the block in L2D format to {@link BufferedOutputStream}. Used only for L2D geodata conversion. -+ * @param stream : The stream. -+ * @throws IOException : Can't save the block to steam. -+ */ -+ public abstract void saveBlock(BufferedOutputStream stream) throws IOException; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (working copy) -@@ -0,0 +1,252 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockComplex(ByteBuffer bb, GeoFormat format) -+ { -+ // initialize buffer -+ _buffer = new byte[GeoStructure.BLOCK_CELLS * 3]; -+ -+ // load data -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // 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); -+ } -+ else -+ { -+ // get nswe -+ final byte nswe = bb.get(); -+ _buffer[i * 3] = nswe; -+ -+ // get height -+ final short height = bb.getShort(); -+ _buffer[(i * 3) + 1] = (byte) (height & 0x00FF); -+ _buffer[(i * 3) + 2] = (byte) (height >> 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height > worldZ ? height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height < worldZ ? height : Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public 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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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 int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_COMPLEX_L2D); -+ -+ // write block data -+ stream.write(_buffer, 0, GeoStructure.BLOCK_CELLS * 3); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (working copy) -@@ -0,0 +1,176 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockFlat extends ABlock -+{ -+ protected final short _height; -+ protected byte _nswe; -+ -+ /** -+ * Creates FlatBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockFlat(ByteBuffer bb, GeoFormat format) -+ { -+ _height = bb.getShort(); -+ _nswe = format != GeoFormat.L2D ? 0x0F : (byte) (0xFF); -+ if (format == GeoFormat.L2OFF) -+ { -+ bb.getShort(); -+ } -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return true; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height > worldZ ? _height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height < worldZ ? _height : Short.MAX_VALUE; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height > worldZ ? _nswe : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height < worldZ ? _nswe : 0; -+ } -+ -+ @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return index -+ return _height < worldZ ? 0 : -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _nswe = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_FLAT_L2D); -+ -+ // write height -+ stream.write((byte) (_height & 0x00FF)); -+ stream.write((byte) (_height >> 8)); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (working copy) -@@ -0,0 +1,465 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+import java.nio.ByteOrder; -+import java.util.Arrays; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockMultilayer extends ABlock -+{ -+ private static final int MAX_LAYERS = Byte.MAX_VALUE; -+ -+ private static ByteBuffer _temp; -+ -+ /** -+ * Initializes the temporarily buffer. -+ */ -+ public static void initialize() -+ { -+ // initialize temporarily buffer and sorting mechanism -+ _temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3); -+ _temp.order(ByteOrder.LITTLE_ENDIAN); -+ } -+ -+ /** -+ * Releases temporarily buffer. -+ */ -+ public static void release() -+ { -+ _temp = null; -+ } -+ -+ protected byte[] _buffer; -+ -+ /** -+ * Implicit constructor for children class. -+ */ -+ protected BlockMultilayer() -+ { -+ _buffer = null; -+ } -+ -+ /** -+ * Creates MultilayerBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockMultilayer(ByteBuffer bb, GeoFormat format) -+ { -+ // 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 = format != GeoFormat.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++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // get data -+ final short data = bb.getShort(); -+ -+ // add nswe and height -+ _temp.put((byte) (data & 0x000F)); -+ _temp.putShort((short) ((short) (data & 0xFFF0) >> 1)); -+ } -+ else -+ { -+ // add nswe -+ _temp.put(bb.get()); -+ -+ // add height -+ _temp.putShort(bb.getShort()); -+ } -+ } -+ } -+ -+ // 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 height -+ if (height > worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, return minimum value -+ return Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 height -+ if (height < worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, return maximum value -+ return Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 nswe -+ if (height > worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 nswe -+ if (height < worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @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 first layer data (first from bottom) -+ byte layers = _buffer[index++]; -+ -+ // loop though all cell layers, find closest layer -+ 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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ // get height -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(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]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ // get nswe -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ // set nswe -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_MULTILAYER_L2D); -+ -+ // for each cell -+ int index = 0; -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ // write layers count -+ final byte layers = _buffer[index++]; -+ stream.write(layers); -+ -+ // write cell data -+ stream.write(_buffer, index, layers * 3); -+ -+ // move index to next cell -+ index += layers * 3; -+ } -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (working copy) -@@ -0,0 +1,150 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockNull extends ABlock -+{ -+ private final byte _nswe; -+ -+ public BlockNull() -+ { -+ _nswe = (byte) 0xFF; -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return false; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweBelow(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) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) -+ { -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (nonexistent) -@@ -1,48 +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 . -- */ --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() -- { -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (nonexistent) -@@ -1,77 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (nonexistent) -@@ -1,56 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (working copy) -@@ -0,0 +1,39 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public enum GeoFormat -+{ -+ L2J("%d_%d.l2j"), -+ L2OFF("%d_%d_conv.dat"), -+ L2D("%d_%d.l2d"); -+ -+ private final String _filename; -+ -+ private GeoFormat(String filename) -+ { -+ _filename = filename; -+ } -+ -+ public String getFilename() -+ { -+ return _filename; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (working copy) -@@ -0,0 +1,67 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.geoengine.GeoEngine; -+import org.l2jmobius.gameserver.model.Location; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoLocation extends Location -+{ -+ private byte _nswe; -+ -+ public GeoLocation(int x, int y, int z) -+ { -+ super(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public void set(int x, int y, short z) -+ { -+ super.setXYZ(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public int getGeoX() -+ { -+ return _x; -+ } -+ -+ public int getGeoY() -+ { -+ return _y; -+ } -+ -+ @Override -+ public int getX() -+ { -+ return GeoEngine.getWorldX(_x); -+ } -+ -+ @Override -+ public int getY() -+ { -+ return GeoEngine.getWorldY(_y); -+ } -+ -+ public byte getNSWE() -+ { -+ return _nswe; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (working copy) -@@ -0,0 +1,70 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoStructure -+{ -+ // cells -+ public static final byte CELL_FLAG_E = 1 << 0; -+ public static final byte CELL_FLAG_W = 1 << 1; -+ public static final byte CELL_FLAG_S = 1 << 2; -+ public static final byte CELL_FLAG_N = 1 << 3; -+ public static final byte CELL_FLAG_SE = 1 << 4; -+ public static final byte CELL_FLAG_SW = 1 << 5; -+ public static final byte CELL_FLAG_NE = 1 << 6; -+ public static final byte CELL_FLAG_NW = (byte) (1 << 7); -+ -+ public static final int CELL_HEIGHT = 8; -+ public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6; -+ -+ // blocks -+ public static final byte TYPE_FLAT_L2J_L2OFF = 0; -+ public static final byte TYPE_FLAT_L2D = (byte) 0xD0; -+ public static final byte TYPE_COMPLEX_L2J = 1; -+ public static final byte TYPE_COMPLEX_L2OFF = 0x40; -+ public static final byte TYPE_COMPLEX_L2D = (byte) 0xD1; -+ 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) -+ public static final byte TYPE_MULTILAYER_L2D = (byte) 0xD2; -+ -+ 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; -+ -+ // regions -+ 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; -+ -+ // global geodata -+ private static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); -+ private 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; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (nonexistent) -@@ -1,42 +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 . -- */ --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); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (working copy) -@@ -0,0 +1,53 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public interface IGeoObject -+{ -+ /** -+ * Returns geodata X coordinate of the {@link IGeoObject}. -+ * @return int : Geodata X coordinate. -+ */ -+ int getGeoX(); -+ -+ /** -+ * Returns geodata Y coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Y coordinate. -+ */ -+ int getGeoY(); -+ -+ /** -+ * Returns geodata Z coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Z coordinate. -+ */ -+ int getGeoZ(); -+ -+ /** -+ * Returns height of the {@link IGeoObject}. -+ * @return int : Height. -+ */ -+ int getHeight(); -+ -+ /** -+ * Returns {@link IGeoObject} data. -+ * @return byte[][] : {@link IGeoObject} data. -+ */ -+ byte[][] getObjectGeoData(); -+} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (nonexistent) -@@ -1,47 +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 . -- */ --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(); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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); -- -- // 1 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (nonexistent) -@@ -1,55 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --/** -- * @author HorridoJoho -- */ --public final class NullRegion implements IRegion --{ -- public static final NullRegion INSTANCE = new NullRegion(); -- -- @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 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Region.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (nonexistent) -@@ -1,92 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (nonexistent) -@@ -1,87 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public abstract class AbstractNode --{ -- private T _loc; -- private AbstractNode _parent; -- -- public AbstractNode(T loc) -- { -- _loc = loc; -- } -- -- public void setParent(AbstractNode p) -- { -- _parent = p; -- } -- -- public AbstractNode getParent() -- { -- return _parent; -- } -- -- public T getLoc() -- { -- return _loc; -- } -- -- public void setLoc(T l) -- { -- _loc = l; -- } -- -- @Override -- public int hashCode() -- { -- final int prime = 31; -- int result = 1; -- result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); -- return result; -- } -- -- @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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (nonexistent) -@@ -1,33 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --/** -- * @author -Nemesiss- -- */ --public abstract class AbstractNodeLoc --{ -- public abstract int getX(); -- -- public abstract int getY(); -- -- public abstract int getZ(); -- -- public abstract int getNodeX(); -- -- public abstract int getNodeY(); --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (nonexistent) -@@ -1,67 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public class CellNode extends AbstractNode --{ -- 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (nonexistent) -@@ -1,348 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --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 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) -- { -- _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(); -- } -- -- public final List debugPath() -- { -- final List 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 void getNeighbors() -- { -- if (!_current.getLoc().canGoAll()) -- { -- 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); -- } -- -- // SouthEast -- if ((nodeE != null) && (nodeS != null)) -- { -- if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) -- { -- addNode(x + 1, y + 1, z, true); -- } -- } -- -- // SouthWest -- if ((nodeS != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) -- { -- addNode(x - 1, y + 1, z, true); -- } -- } -- -- // NorthEast -- if ((nodeN != null) && (nodeE != null)) -- { -- if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) -- { -- addNode(x + 1, y - 1, z, true); -- } -- } -- -- // NorthWest -- if ((nodeN != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) -- { -- addNode(x - 1, y - 1, z, true); -- } -- } -- } -- -- private 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(); -- // reinit node if needed -- if (result.getLoc() != null) -- { -- result.getLoc().set(x, y, z); -- } -- else -- { -- result.setLoc(new NodeLoc(x, y, z)); -- } -- } -- -- return result; -- } -- -- private 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 boolean isHighWeight(int x, int y, int z) -- { -- final CellNode result = getNode(x, y, z); -- if (result == null) -- { -- return true; -- } -- -- if (!result.getLoc().canGoAll()) -- { -- return true; -- } -- if (Math.abs(result.getLoc().getZ() - z) > 16) -- { -- return true; -- } -- -- return false; -- } -- -- private 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (working copy) -@@ -0,0 +1,87 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+ -+/** -+ * @author Hasha -+ */ -+public class Node -+{ -+ // node coords and nswe flag -+ private GeoLocation _loc; -+ -+ // node parent (for reverse path construction) -+ private Node _parent; -+ // node child (for moving over nodes during iteration) -+ private Node _child; -+ -+ // node G cost (movement cost = parent movement cost + current movement cost) -+ private double _cost = -1000; -+ -+ public void setLoc(int x, int y, int z) -+ { -+ _loc = new GeoLocation(x, y, z); -+ } -+ -+ public GeoLocation getLoc() -+ { -+ return _loc; -+ } -+ -+ public void setParent(Node parent) -+ { -+ _parent = parent; -+ } -+ -+ public Node getParent() -+ { -+ return _parent; -+ } -+ -+ public void setChild(Node child) -+ { -+ _child = child; -+ } -+ -+ public Node getChild() -+ { -+ return _child; -+ } -+ -+ public void setCost(double cost) -+ { -+ _cost = cost; -+ } -+ -+ public double getCost() -+ { -+ return _cost; -+ } -+ -+ public void free() -+ { -+ // reset node location -+ _loc = null; -+ -+ // reset node parent, child and cost -+ _parent = null; -+ _child = null; -+ _cost = -1000; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (working copy) -@@ -0,0 +1,306 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import java.util.concurrent.locks.ReentrantLock; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+ -+/** -+ * @author DS, Hasha; Credits to Diamond -+ */ -+public class NodeBuffer -+{ -+ private final ReentrantLock _lock = new ReentrantLock(); -+ private final int _size; -+ private final Node[][] _buffer; -+ -+ // center coordinates -+ private int _cx = 0; -+ private int _cy = 0; -+ -+ // target coordinates -+ private int _gtx = 0; -+ private int _gty = 0; -+ private short _gtz = 0; -+ -+ private Node _current = null; -+ -+ /** -+ * Constructor of NodeBuffer. -+ * @param size : one dimension size of buffer -+ */ -+ public NodeBuffer(int size) -+ { -+ // set size -+ _size = size; -+ -+ // initialize buffer -+ _buffer = new Node[size][size]; -+ for (int x = 0; x < size; x++) -+ { -+ for (int y = 0; y < size; y++) -+ { -+ _buffer[x][y] = 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 Node : first node of path -+ */ -+ public Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) -+ { -+ // set coordinates (middle of the line (gox,goy) - (gtx,gty), will be in the center of the buffer) -+ _cx = gox + ((gtx - gox - _size) / 2); -+ _cy = goy + ((gty - goy - _size) / 2); -+ -+ _gtx = gtx; -+ _gty = gty; -+ _gtz = gtz; -+ -+ _current = getNode(gox, goy, goz); -+ _current.setCost(getCostH(gox, goy, goz)); -+ -+ int count = 0; -+ do -+ { -+ // reached target? -+ if ((_current.getLoc().getGeoX() == _gtx) && (_current.getLoc().getGeoY() == _gty) && (Math.abs(_current.getLoc().getZ() - _gtz) < 8)) -+ { -+ return _current; -+ } -+ -+ // expand current node -+ expand(); -+ -+ // move pointer -+ _current = _current.getChild(); -+ } -+ while ((_current != null) && (count++ < Config.MAX_ITERATIONS)); -+ -+ return null; -+ } -+ -+ public boolean isLocked() -+ { -+ return _lock.tryLock(); -+ } -+ -+ public void free() -+ { -+ _current = null; -+ -+ for (Node[] nodes : _buffer) -+ { -+ for (Node node : nodes) -+ { -+ if (node.getLoc() != null) -+ { -+ node.free(); -+ } -+ } -+ } -+ -+ _lock.unlock(); -+ } -+ -+ /** -+ * Check _current Node and add its neighbors to the buffer. -+ */ -+ private final void expand() -+ { -+ // can't move anywhere, don't expand -+ final byte nswe = _current.getLoc().getNSWE(); -+ if (nswe == 0) -+ { -+ return; -+ } -+ -+ // get geo coords of the node to be expanded -+ final int x = _current.getLoc().getGeoX(); -+ final int y = _current.getLoc().getGeoY(); -+ final short z = (short) _current.getLoc().getZ(); -+ -+ // can move north, expand -+ if ((nswe & GeoStructure.CELL_FLAG_N) != 0) -+ { -+ addNode(x, y - 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move south, expand -+ if ((nswe & GeoStructure.CELL_FLAG_S) != 0) -+ { -+ addNode(x, y + 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_W) != 0) -+ { -+ addNode(x - 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_E) != 0) -+ { -+ addNode(x + 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move north-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NW) != 0) -+ { -+ addNode(x - 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move north-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NE) != 0) -+ { -+ addNode(x + 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SW) != 0) -+ { -+ addNode(x - 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SE) != 0) -+ { -+ addNode(x + 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ } -+ -+ /** -+ * Returns node, if it exists in buffer. -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param z : node Z coord -+ * @return Node : node, if exits in buffer -+ */ -+ private final Node getNode(int x, int y, short z) -+ { -+ // check node X out of coordinates -+ final int ix = x - _cx; -+ if ((ix < 0) || (ix >= _size)) -+ { -+ return null; -+ } -+ -+ // check node Y out of coordinates -+ final int iy = y - _cy; -+ if ((iy < 0) || (iy >= _size)) -+ { -+ return null; -+ } -+ -+ // get node -+ final Node result = _buffer[ix][iy]; -+ -+ // check and update -+ if (result.getLoc() == null) -+ { -+ result.setLoc(x, y, z); -+ } -+ -+ // return node -+ return result; -+ } -+ -+ /** -+ * Add node given by coordinates to the buffer. -+ * @param x : geo X coord -+ * @param y : geo Y coord -+ * @param z : geo Z coord -+ * @param weight : weight of movement to new node -+ */ -+ private final void addNode(int x, int y, short z, int weight) -+ { -+ // get node to be expanded -+ final Node node = getNode(x, y, z); -+ if (node == null) -+ { -+ return; -+ } -+ -+ // Z distance between nearby cells is higher than cell size -+ if (node.getLoc().getZ() > (z + (2 * GeoStructure.CELL_HEIGHT))) -+ { -+ return; -+ } -+ -+ // node was already expanded, return -+ if (node.getCost() >= 0) -+ { -+ return; -+ } -+ -+ node.setParent(_current); -+ if (node.getLoc().getNSWE() != (byte) 0xFF) -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + (weight * Config.OBSTACLE_MULTIPLIER)); -+ } -+ else -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + weight); -+ } -+ -+ Node current = _current; -+ int count = 0; -+ while ((current.getChild() != null) && (count < (Config.MAX_ITERATIONS * 4))) -+ { -+ count++; -+ if (current.getChild().getCost() > node.getCost()) -+ { -+ node.setChild(current.getChild()); -+ break; -+ } -+ current = current.getChild(); -+ } -+ -+ if (count >= (Config.MAX_ITERATIONS * 4)) -+ { -+ System.err.println("Pathfinding: too long loop detected, cost:" + node.getCost()); -+ } -+ -+ current.setChild(node); -+ } -+ -+ /** -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param i : node Z coord -+ * @return double : node cost -+ */ -+ private final double getCostH(int x, int y, int i) -+ { -+ final int dX = x - _gtx; -+ final int dY = y - _gty; -+ final int dZ = (i - _gtz) / GeoStructure.CELL_HEIGHT; -+ -+ // return (Math.abs(dX) + Math.abs(dY) + Math.abs(dZ)) * Config.HEURISTIC_WEIGHT; // Manhattan distance -+ return Math.sqrt((dX * dX) + (dY * dY) + (dZ * dZ)) * Config.HEURISTIC_WEIGHT; // Direct distance -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -- --/** -- * @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 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 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; -- // return super.hashCode(); -- } -- -- @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; -- if (_x != other._x) -- { -- return false; -- } -- if (_y != other._y) -- { -- return false; -- } -- if (_goNorth != other._goNorth) -- { -- return false; -- } -- if (_goEast != other._goEast) -- { -- return false; -- } -- if (_goSouth != other._goSouth) -- { -- return false; -- } -- if (_goWest != other._goWest) -- { -- return false; -- } -- if (_geoHeight != other._geoHeight) -- { -- return false; -- } -- return true; -- } --} -Index: java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeodata.java -=================================================================== ---- java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeodata.java (revision 8319) -+++ java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeodata.java (working copy) -@@ -47,8 +47,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -@@ -63,8 +63,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -Index: java/org/l2jmobius/gameserver/model/actor/Creature.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/Creature.java (revision 8399) -+++ java/org/l2jmobius/gameserver/model/actor/Creature.java (working copy) -@@ -44,8 +44,6 @@ - import org.l2jmobius.gameserver.data.xml.ZoneData; - import org.l2jmobius.gameserver.enums.TeleportWhereType; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; - import org.l2jmobius.gameserver.handler.ISkillHandler; - import org.l2jmobius.gameserver.handler.SkillHandler; - import org.l2jmobius.gameserver.handler.itemhandlers.Potions; -@@ -4298,7 +4296,7 @@ - public int _heading; - public boolean disregardingGeodata; - public int onGeodataPathIndex; -- public List geoPath; -+ public List geoPath; - public int geoPathAccurateTx; - public int geoPathAccurateTy; - public int geoPathGtx; -@@ -5578,7 +5576,7 @@ - if (((originalDistance - distance) > 30) && !_isAfraid && !isInBoat) - { - // Path calculation -- overrides previous movement check -- m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId()); -+ m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId()); - if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found - { - if (isPlayer() && !_isFlying && !isInWater) -Index: java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (revision 8352) -+++ java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (working copy) -@@ -15483,7 +15483,7 @@ - { - if (Config.CORRECT_PLAYER_Z) - { -- final int nearestZ = GeoEngine.getInstance().getHigherHeight(getX(), getY(), getZ()); -+ final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); - if (getZ() < nearestZ) - { - teleToLocation(new Location(getX(), getY(), nearestZ), false); -Index: java/org/l2jmobius/gameserver/util/GeoUtils.java -=================================================================== ---- java/org/l2jmobius/gameserver/util/GeoUtils.java (revision 8319) -+++ java/org/l2jmobius/gameserver/util/GeoUtils.java (working copy) -@@ -19,7 +19,7 @@ - import java.awt.Color; - - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; - import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; - -@@ -26,25 +26,25 @@ - /** - * @author HorridoJoho - */ --public final class GeoUtils -+public class GeoUtils - { - public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); - - final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); - - while (iter.next()) - { -- final int wx = GeoEngine.getInstance().getWorldX(iter.x()); -- final int wy = GeoEngine.getInstance().getWorldY(iter.y()); -+ final int wx = GeoEngine.getWorldX(iter.x()); -+ final int wy = GeoEngine.getWorldY(iter.y()); - - prim.addPoint(Color.RED, wx, wy, z); - } -@@ -53,21 +53,21 @@ - - public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); -- int wy = GeoEngine.getInstance().getWorldY(prevY); -+ int wx = GeoEngine.getWorldX(prevX); -+ int wy = GeoEngine.getWorldY(prevY); - int wz = iter.z(); - prim.addPoint(Color.RED, wx, wy, wz); - -@@ -78,8 +78,8 @@ - - if ((curX != prevX) || (curY != prevY)) - { -- wx = GeoEngine.getInstance().getWorldX(curX); -- wy = GeoEngine.getInstance().getWorldY(curY); -+ wx = GeoEngine.getWorldX(curX); -+ wy = GeoEngine.getWorldY(curY); - wz = iter.z(); - - prim.addPoint(Color.RED, wx, wy, wz); -@@ -93,7 +93,7 @@ - - private static Color getDirectionColor(int x, int y, int z, int nswe) - { -- if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) -+ if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) - { - return Color.GREEN; - } -@@ -109,9 +109,8 @@ - int iPacket = 0; - - ExServerPrimitive exsp = null; -- final GeoEngine ge = GeoEngine.getInstance(); -- final int playerGx = ge.getGeoX(player.getX()); -- final int playerGy = ge.getGeoY(player.getY()); -+ final int playerGx = GeoEngine.getGeoX(player.getX()); -+ final int playerGy = GeoEngine.getGeoY(player.getY()); - for (int dx = -geoRadius; dx <= geoRadius; ++dx) - { - for (int dy = -geoRadius; dy <= geoRadius; ++dy) -@@ -135,12 +134,12 @@ - final int gx = playerGx + dx; - final int gy = playerGy + dy; - -- final int x = ge.getWorldX(gx); -- final int y = ge.getWorldY(gy); -- final int z = ge.getNearestZ(gx, gy, player.getZ()); -+ final int x = GeoEngine.getWorldX(gx); -+ final int y = GeoEngine.getWorldY(gy); -+ final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); - - // north arrow -- Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); -+ Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); - 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); -@@ -147,7 +146,7 @@ - exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z); - - // east arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); - 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); -@@ -154,13 +153,13 @@ - exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z); - - // south arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); - 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, Cell.NSWE_WEST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); - 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); -@@ -188,15 +187,15 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_EAST; -+ return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_EAST; -+ return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; - } - else - { -- return Cell.NSWE_EAST; -+ return GeoStructure.CELL_FLAG_E; // Direction.EAST; - } - } - else if (x < lastX) // west -@@ -203,26 +202,27 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_WEST; -+ return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_WEST; -+ return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; - } - else - { -- return Cell.NSWE_WEST; -+ return GeoStructure.CELL_FLAG_W; // Direction.WEST; - } - } -- else // unchanged x -+ else -+ // unchanged x - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH; -+ return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH; -+ return GeoStructure.CELL_FLAG_N; // Direction.NORTH; - } - else - { -Index: java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java -=================================================================== ---- java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (nonexistent) -+++ java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (working copy) -@@ -0,0 +1,367 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.tools.geodataconverter; -+ -+import java.io.BufferedOutputStream; -+import java.io.File; -+import java.io.FileOutputStream; -+import java.io.RandomAccessFile; -+import java.nio.ByteOrder; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.Scanner; -+ -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoDataConverter -+{ -+ final static String GEODATA_PATH = "./data/geodata/"; -+ -+ private static GeoFormat _format; -+ private static ABlock[][] _blocks; -+ -+ public static void main(String[] args) -+ { -+ // get geodata format -+ String type = ""; -+ try (Scanner scn = new Scanner(System.in)) -+ { -+ while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) -+ { -+ System.out.println("GeoDataConverter: Select source geodata type:"); -+ System.out.println(" J: L2J (e.g. 23_22.l2j)"); -+ System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); -+ System.out.println(" E: Exit"); -+ System.out.print("Choice: "); -+ type = scn.next(); -+ } -+ } -+ if (type.equalsIgnoreCase("E")) -+ { -+ System.exit(0); -+ } -+ -+ _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; -+ -+ // start conversion -+ System.out.println("GeoDataConverter: Converting all " + _format + " files."); -+ -+ // initialize geodata container -+ _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files -+ int converted = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) -+ { -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) -+ { -+ final String input = String.format(_format.getFilename(), rx, ry); -+ final String filepath = GEODATA_PATH; -+ final File f = new File(filepath + input); -+ if (f.exists() && !f.isDirectory()) -+ { -+ // load geodata -+ if (!loadGeoBlocks(input)) -+ { -+ System.out.println("GeoDataConverter: Unable to load " + input + " region file."); -+ continue; -+ } -+ -+ // recalculate nswe -+ if (!recalculateNswe()) -+ { -+ System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); -+ continue; -+ } -+ -+ // save geodata -+ final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); -+ if (!saveGeoBlocks(output)) -+ { -+ System.out.println("GeoDataConverter: Unable to save " + output + " region file."); -+ continue; -+ } -+ -+ converted++; -+ System.out.println("GeoDataConverter: Created " + output + " region file."); -+ } -+ } -+ } -+ System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); -+ } -+ -+ /** -+ * Loads geo blocks from buffer of the region file. -+ * @param filename : The name of the to load. -+ * @return boolean : True when successful. -+ */ -+ private static boolean loadGeoBlocks(String filename) -+ { -+ // region file is load-able, try to load it -+ try (RandomAccessFile raf = new RandomAccessFile(GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) -+ { -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) -+ if (_format == GeoFormat.L2OFF) -+ { -+ for (int i = 0; i < 18; i++) -+ { -+ buffer.get(); -+ } -+ } -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ if (_format == GeoFormat.L2J) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2J: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2J: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ else -+ { -+ // get block type -+ final short type = buffer.getShort(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ default: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ if (buffer.remaining() > 0) -+ { -+ System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ return false; -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); -+ return false; -+ } -+ } -+ -+ /** -+ * Recalculate diagonal flags for the region file. -+ * @return boolean : True when successful. -+ */ -+ private static boolean recalculateNswe() -+ { -+ try -+ { -+ for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) -+ { -+ for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) -+ { -+ // get block -+ final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // skip flat blocks -+ if (block instanceof BlockFlat) -+ { -+ continue; -+ } -+ -+ // for complex and multilayer blocks go though all layers -+ short height = Short.MAX_VALUE; -+ int index; -+ while ((index = block.getIndexBelow(x, y, height)) != -1) -+ { -+ // get height and nswe -+ height = block.getHeight(index); -+ byte nswe = block.getNswe(index); -+ -+ // update nswe with diagonal flags -+ nswe = updateNsweBelow(x, y, height, nswe); -+ -+ // set nswe of the cell -+ block.setNswe(index, nswe); -+ } -+ } -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ /** -+ * Updates the NSWE flag with diagonal flags. -+ * @param x : Geodata X coordinate. -+ * @param y : Geodata Y coordinate. -+ * @param z : Geodata Z coordinate. -+ * @param nsweValue : NSWE flag to be updated. -+ * @return byte : Updated NSWE flag. -+ */ -+ private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) -+ { -+ byte nswe = nsweValue; -+ -+ // calculate virtual layer height -+ final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); -+ -+ // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) -+ final byte nsweN = getNsweBelow(x, y - 1, height); -+ final byte nsweS = getNsweBelow(x, y + 1, height); -+ final byte nsweW = getNsweBelow(x - 1, y, height); -+ final byte nsweE = getNsweBelow(x + 1, y, height); -+ -+ // north-west -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NW; -+ } -+ -+ // north-east -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NE; -+ } -+ -+ // south-west -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SW; -+ } -+ -+ // south-east -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SE; -+ } -+ -+ return nswe; -+ } -+ -+ private static byte getNsweBelow(int geoX, int geoY, short worldZ) -+ { -+ // out of geo coordinates -+ if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) -+ { -+ return 0; -+ } -+ -+ // out of geo coordinates -+ if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) -+ { -+ return 0; -+ } -+ -+ // get block -+ final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // get index, when valid, return nswe -+ final int index = block.getIndexBelow(geoX, geoY, worldZ); -+ return index == -1 ? 0 : block.getNswe(index); -+ } -+ -+ /** -+ * Save region file to file. -+ * @param filename : The name of file to save. -+ * @return boolean : True when successful. -+ */ -+ private static boolean saveGeoBlocks(String filename) -+ { -+ try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) -+ { -+ // loop over region blocks and save each block -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[ix][iy].saveBlock(bos); -+ } -+ } -+ -+ // flush data to file -+ bos.flush(); -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+} -\ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/dist/game/data/geodata/Readme.txt b/L2J_Mobius_C4_ScionsOfDestiny/dist/game/data/geodata/Readme.txt index 5c756621e5..1e29544602 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/dist/game/data/geodata/Readme.txt +++ b/L2J_Mobius_C4_ScionsOfDestiny/dist/game/data/geodata/Readme.txt @@ -23,8 +23,8 @@ a - Prerequisites b - Make it work ---------------------------------------------- -To make geodata working: -* unpack your geodata files into "/data/geodata" folder -* open "/config/main/GeoEngine.ini" with your favorite text editor and then edit following config: - - CoordSynchronize = 2 -* If you do not use any geodata files, the server will automatically change this setting to -1. +To make geodata work: +* unpack your geodata files into "/data/geodata" folder (or any other folder) +* open "/config/main/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. diff --git a/L2J_Mobius_C4_ScionsOfDestiny/dist/game/data/scripts/ai/bosses/Antharas.java b/L2J_Mobius_C4_ScionsOfDestiny/dist/game/data/scripts/ai/bosses/Antharas.java index 46d3bcafec..a175651d2f 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/dist/game/data/scripts/ai/bosses/Antharas.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/dist/game/data/scripts/ai/bosses/Antharas.java @@ -620,7 +620,7 @@ public class Antharas extends Quest final int ry = Rnd.get(112400, 116000); final int rdt = ((_antharas.getX() - rx) * (_antharas.getX() - rx)) + ((_antharas.getY() - ry) * (_antharas.getY() - ry)); final Location randomLocation = new Location(rx, ry, -7704); - if (GeoEngine.getInstance().canSeeTarget(_antharas, randomLocation) && (rdt < dt)) + if (GeoEngine.getInstance().canSeeLocation(_antharas, randomLocation) && (rdt < dt)) { x = rx; y = ry; diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/Config.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/Config.java index 1037d1b146..7fc84700fb 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/Config.java @@ -41,6 +41,7 @@ import org.l2jmobius.commons.enums.ServerMode; import org.l2jmobius.commons.util.ClassMasterSettings; import org.l2jmobius.commons.util.PropertiesParser; import org.l2jmobius.commons.util.StringUtil; +import org.l2jmobius.gameserver.enums.GeoType; import org.l2jmobius.gameserver.model.olympiad.OlympiadPeriod; import org.l2jmobius.gameserver.util.FloodProtectorConfig; import org.l2jmobius.gameserver.util.Util; @@ -931,12 +932,17 @@ public class Config public static boolean ALT_RAIDS_STATS_BONUS; public static Path GEODATA_PATH; + public static GeoType GEODATA_TYPE; public static boolean PATHFINDING; public static String PATHFIND_BUFFERS; - public static float LOW_WEIGHT; - public static float MEDIUM_WEIGHT; - public static float HIGH_WEIGHT; - public static float DIAGONAL_WEIGHT; + public static int MOVE_WEIGHT; + public static int MOVE_WEIGHT_DIAG; + public static int OBSTACLE_WEIGHT; + 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 boolean FALL_DAMAGE; public static boolean ALLOW_WATER; @@ -2494,12 +2500,17 @@ public class Config { 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", "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); - DIAGONAL_WEIGHT = geoengineConfig.getFloat("DiagonalWeight", 0.707f); + PATHFIND_BUFFERS = geoengineConfig.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + MOVE_WEIGHT = geoengineConfig.getInt("MoveWeight", 10); + MOVE_WEIGHT_DIAG = geoengineConfig.getInt("MoveWeightDiag", 14); + OBSTACLE_WEIGHT = geoengineConfig.getInt("ObstacleWeight", 30); + 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); FALL_DAMAGE = geoengineConfig.getBoolean("FallDamage", false); ALLOW_WATER = geoengineConfig.getBoolean("AllowWater", false); } diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/commons/util/Point2D.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/commons/util/Point2D.java new file mode 100644 index 0000000000..ebb6272e5e --- /dev/null +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/commons/util/Point2D.java @@ -0,0 +1,180 @@ +/* + * 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 . + */ +package org.l2jmobius.commons.util; + +import java.util.Objects; + +/** + * A datatype used to retain a 2D (x/y) point. It got the capability to be set and cleaned. + */ +public class Point2D +{ + protected volatile int _x; + protected volatile int _y; + + public Point2D(int x, int y) + { + _x = x; + _y = y; + } + + @Override + public Point2D clone() + { + return new Point2D(_x, _y); + } + + @Override + public String toString() + { + return _x + ", " + _y; + } + + @Override + public int hashCode() + { + return Objects.hash(_x, _y); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final Point2D other = (Point2D) obj; + return (_x == other._x) && (_y == other._y); + } + + /** + * @param x : The X coord to test. + * @param y : The Y coord to test. + * @return True if all coordinates equals this {@link Point2D} coordinates. + */ + public boolean equals(int x, int y) + { + return (_x == x) && (_y == y); + } + + public int getX() + { + return _x; + } + + public void setX(int x) + { + _x = x; + } + + public int getY() + { + return _y; + } + + public void setY(int y) + { + _y = y; + } + + public void set(int x, int y) + { + _x = x; + _y = y; + } + + /** + * Refresh the current {@link Point2D} using a reference {@link Point2D} and a distance. The new destination is calculated to go in opposite side of the {@link Point2D} reference.
+ *
+ * This method is perfect to calculate fleeing characters position. + * @param referenceLoc : The Point2D used as position. + * @param distance : The distance to be set between current and new position. + */ + public void setFleeing(Point2D referenceLoc, int distance) + { + final double xDiff = referenceLoc.getX() - _x; + final double yDiff = referenceLoc.getY() - _y; + + final double yxRation = Math.abs(xDiff / yDiff); + + final int y = (int) (distance / (yxRation + 1)); + final int x = (int) (y * yxRation); + + _x += (xDiff < 0 ? x : -x); + _y += (yDiff < 0 ? y : -y); + } + + public void clean() + { + _x = 0; + _y = 0; + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @return The distance between this {@Point2D} and some given coordinates. + */ + public double distance2D(int x, int y) + { + final double dx = (double) _x - x; + final double dy = (double) _y - y; + + return Math.sqrt((dx * dx) + (dy * dy)); + } + + /** + * @param point : The {@link Point2D} to test. + * @return The distance between this {@Point2D} and the {@link Point2D} set as parameter. + */ + public double distance2D(Point2D point) + { + return distance2D(point.getX(), point.getY()); + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of some given coordinates. + */ + public boolean isIn2DRadius(int x, int y, int radius) + { + return distance2D(x, y) < radius; + } + + /** + * @param point : The Point2D to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of the {@link Point2D} set as parameter. + */ + public boolean isIn2DRadius(Point2D point, int radius) + { + return distance2D(point) < radius; + } +} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/data/xml/DoorData.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/data/xml/DoorData.java index 71fb74f06b..40539cebae 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/data/xml/DoorData.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/data/xml/DoorData.java @@ -234,12 +234,12 @@ public class DoorData implements IXmlReader } } - public boolean checkIfDoorsBetween(Location start, Location end) + public boolean checkIfDoorsBetween(Location start, Location end, int instanceId) { - return checkIfDoorsBetween(start.getX(), start.getY(), start.getZ(), end.getX(), end.getY(), end.getZ()); + return checkIfDoorsBetween(start.getX(), start.getY(), start.getZ(), end.getX(), end.getY(), end.getZ(), instanceId); } - public boolean checkIfDoorsBetween(int x, int y, int z, int tx, int ty, int tz) + public boolean checkIfDoorsBetween(int x, int y, int z, int tx, int ty, int tz, int instanceId) { final WorldRegion region = World.getInstance().getRegion(x, y); final Collection doors = region != null ? region.getDoors() : null; @@ -248,9 +248,9 @@ public class DoorData implements IXmlReader return false; } - for (DoorInstance doorInst : doors) + for (DoorInstance door : doors) { - if (doorInst.getXMax() == 0) + if ((door.getXMax() == 0) || (door.getInstanceId() != instanceId)) { continue; } @@ -259,32 +259,32 @@ public class DoorData implements IXmlReader // heavy approximation disabling some shooting angles especially near 2-piece doors // but most calculations should stop short // phase 1, x - if (((x <= doorInst.getXMax()) && (tx >= doorInst.getXMin())) || ((tx <= doorInst.getXMax()) && (x >= doorInst.getXMin()))) + if (((x <= door.getXMax()) && (tx >= door.getXMin())) || ((tx <= door.getXMax()) && (x >= door.getXMin()))) { // phase 2, y - if (((y <= doorInst.getYMax()) && (ty >= doorInst.getYMin())) || ((ty <= doorInst.getYMax()) && (y >= doorInst.getYMin()))) + if (((y <= door.getYMax()) && (ty >= door.getYMin())) || ((ty <= door.getYMax()) && (y >= door.getYMin()))) { // phase 3, basically only z remains but now we calculate it with another formula (by rage) // in some cases the direct line check (only) in the beginning isn't sufficient, // when char z changes a lot along the path - if ((doorInst.getStatus().getCurrentHp() > 0) && !doorInst.isOpen()) + if ((door.getStatus().getCurrentHp() > 0) && !door.isOpen()) { - final int px1 = doorInst.getXMin(); - final int py1 = doorInst.getYMin(); - final int pz1 = doorInst.getZMin(); - final int px2 = doorInst.getXMax(); - final int py2 = doorInst.getYMax(); - final int pz2 = doorInst.getZMax(); + final int px1 = door.getXMin(); + final int py1 = door.getYMin(); + final int pz1 = door.getZMin(); + final int px2 = door.getXMax(); + final int py2 = door.getYMax(); + final int pz2 = door.getZMax(); final int l = tx - x; final int m = ty - y; final int n = tz - z; - final int dk = ((doorInst.getA() * l) + (doorInst.getB() * m) + (doorInst.getC() * n)); + final int dk = ((door.getA() * l) + (door.getB() * m) + (door.getC() * n)); if (dk == 0) { continue; // Parallel } - final float p = (float) ((doorInst.getA() * x) + (doorInst.getB() * y) + (doorInst.getC() * z) + doorInst.getD()) / dk; + final float p = (float) ((door.getA() * x) + (door.getB() * y) + (door.getC() * z) + door.getD()) / dk; final int fx = (int) (x - (l * p)); final int fy = (int) (y - (m * p)); final int fz = (int) (z - (n * p)); diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/data/xml/FenceData.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/data/xml/FenceData.java index 924fca51ef..804598e7b5 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/data/xml/FenceData.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/data/xml/FenceData.java @@ -139,7 +139,7 @@ public class FenceData return _fences.get(objectId); } - public boolean checkIfFenceBetween(int x, int y, int z, int tx, int ty, int tz) + public boolean checkIfFenceBetween(int x, int y, int z, int tx, int ty, int tz, int instanceId) { final WorldRegion region = World.getInstance().getRegion(x, y); final List fences = region != null ? region.getFences() : null; @@ -151,7 +151,7 @@ public class FenceData for (FenceInstance fence : fences) { // Check if fence is geodata enabled. - if (!fence.getState().isGeodataEnabled()) + if (!fence.getState().isGeodataEnabled() || (fence.getInstanceId() != instanceId)) { continue; } diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/enums/GeoType.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/enums/GeoType.java new file mode 100644 index 0000000000..f77aa5eac4 --- /dev/null +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/enums/GeoType.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.enums; + +public enum GeoType +{ + L2J("%d_%d.l2j"), + L2OFF("%d_%d_conv.dat"); + + private final String _filename; + + private GeoType(String filename) + { + _filename = filename; + } + + public String getFilename() + { + return _filename; + } +} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java new file mode 100644 index 0000000000..e62f50a8df --- /dev/null +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java @@ -0,0 +1,144 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index b561f7dbed..17f2ed17d8 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,70 +16,62 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.channels.FileChannel.MapMode; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.logging.Level; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; -import org.l2jmobius.gameserver.geoengine.geodata.IRegion; -import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; -import org.l2jmobius.gameserver.geoengine.geodata.Region; +import org.l2jmobius.gameserver.enums.GeoType; +import org.l2jmobius.gameserver.enums.MoveDirectionType; +import org.l2jmobius.gameserver.geoengine.geodata.ABlock; +import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; +import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; +import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; +import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; -import org.l2jmobius.gameserver.util.GeoUtils; -import org.l2jmobius.gameserver.util.LinePointIterator; -import org.l2jmobius.gameserver.util.LinePointIterator3D; +import org.l2jmobius.gameserver.model.actor.Creature; +import org.l2jmobius.gameserver.util.Util; -/** - * @author -Nemesiss-, HorridoJoho - */ public class GeoEngine { - private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - - private static final int WORLD_MIN_X = -655360; - private static final int WORLD_MIN_Y = -589824; - private static final int WORLD_MIN_Z = -16384; - - /** 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; - - /** The regions array */ - private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; - private static final int ELEVATED_SEE_OVER_DISTANCE = 2; - private static final int MAX_SEE_OVER_HEIGHT = 48; - protected GeoEngine() + private final ABlock[][] _blocks; + private final BlockNull _nullBlock; + + // Pre-allocated buffers. + private final BufferHolder[] _buffers; + + public GeoEngine() { LOGGER.info("GeoEngine: Initializing..."); - for (int i = 0; i < _regions.length(); i++) - { - _regions.set(i, NullRegion.INSTANCE); - } + // Initialize block container. + _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; + + // Load null block. + _nullBlock = new BlockNull(); + + // Initialize multilayer temporarily buffer. + BlockMultilayer.initialize(); + + // Load geo files according to geoengine config setup. int loaded = 0; try { @@ -90,23 +82,52 @@ public class GeoEngine final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); if (Files.exists(geoFilePath)) { - try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + // Region file is load-able, try to load it. + if (loadGeoBlocks(regionX, regionY)) { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); loaded++; } } + else + { + // Region file is not load-able, load null blocks. + loadNullBlocks(regionX, regionY); + } } } } catch (Exception e) { - LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + LOGGER.warning("GeoEngine: Failed to load geodata! " + e); System.exit(1); } - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + // Release multilayer block temporarily buffer. + BlockMultilayer.release(); + + String[] array = Config.PATHFIND_BUFFERS.split(";"); + _buffers = new BufferHolder[array.length]; + + int count = 0; + for (int i = 0; i < array.length; i++) + { + String buf = array[i]; + String[] args = buf.split("x"); + + try + { + int size = Integer.parseInt(args[1]); + count += size; + _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); + } + catch (Exception e) + { + LOGGER.warning("Could not load buffer setting:" + buf + ". " + e); + } + } + LOGGER.info("Loaded " + count + " node buffers."); + // Avoid wrong configs when no files are loaded. if ((loaded == 0) && Config.PATHFINDING) { @@ -116,616 +137,913 @@ public class GeoEngine } /** - * @param geoX - * @param geoY - * @return the region + * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer and log this situation. + * @param size : pre-calculated minimal required size + * @return NodeBuffer : buffer */ - private IRegion getRegion(int geoX, int geoY) + private NodeBuffer getBuffer(int size) { - final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); - if ((region < 0) || (region >= _regions.length())) + NodeBuffer current = null; + for (BufferHolder holder : _buffers) { - return null; - } - return _regions.get(region); - } - - /** - * @param filePath - * @param regionX - * @param regionY - * @throws IOException - */ - 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()).load().order(ByteOrder.LITTLE_ENDIAN))); - } - } - - /** - * @param regionX - * @param regionY - */ - public void unloadRegion(int regionX, int regionY) - { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); - } - - /** - * @param geoX - * @param geoY - * @return if geodata exist - */ - public boolean hasGeoPos(int geoX, int geoY) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return false; - } - return region.hasGeo(); - } - - /** - * Checks the specified position for available geodata. - * @param x the world x - * @param y the world y - * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise - */ - public boolean hasGeo(int x, int y) - { - return hasGeoPos(getGeoX(x), getGeoY(y)); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe check - */ - public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return true; - } - return region.checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe anti-corner cut - */ - public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) - { - boolean can = true; - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - return can && checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the nearest Z value - */ - public int getNearestZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNearestZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next lower Z value - */ - public int getNextLowerZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextLowerZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next higher Z value - */ - public int getNextHigherZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextHigherZ(geoX, geoY, worldZ); - } - - /** - * Gets the Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHeight(int x, int y, int z) - { - return getNearestZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next lower Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getLowerHeight(int x, int y, int z) - { - return getNextLowerZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next higher Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHigherHeight(int x, int y, int z) - { - return getNextHigherZ(getGeoX(x), getGeoY(y), z); - } - - /** - * @param worldX - * @return the geo X - */ - public int getGeoX(int worldX) - { - return (worldX - WORLD_MIN_X) / 16; - } - - /** - * @param worldY - * @return the geo Y - */ - public int getGeoY(int worldY) - { - return (worldY - WORLD_MIN_Y) / 16; - } - - /** - * @param worldZ - * @return the geo Z - */ - public int getGeoZ(int worldZ) - { - return (worldZ - WORLD_MIN_Z) / 16; - } - - /** - * @param geoX - * @return the world X - */ - public int getWorldX(int geoX) - { - return (geoX * 16) + WORLD_MIN_X + 8; - } - - /** - * @param geoY - * @return the world Y - */ - public int getWorldY(int geoY) - { - return (geoY * 16) + WORLD_MIN_Y + 8; - } - - /** - * @param geoZ - * @return the world Z - */ - public int getWorldZ(int geoZ) - { - return (geoZ * 16) + WORLD_MIN_Z + 8; - } - - /** - * Can see target. Doors as target always return true. Checks doors between. - * @param cha the character - * @param target the target - * @return {@code true} if the character can see the target (LOS), {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, WorldObject target) - { - if (target.isDoor()) - { - // Can always see doors. - return true; - } - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), target.getX(), target.getY(), target.getZ(), target.getInstanceId()); - } - - /** - * Can see target. Checks doors between. - * @param cha the character - * @param worldPosition the world position - * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, Location worldPosition) - { - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instanceId - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @param tInstanceId the target's instanceId - * @return - */ - public boolean canSeeTarget(int x, int y, int z, int instanceId, int tx, int ty, int tz, int tInstanceId) - { - return (instanceId != tInstanceId) ? false : canSeeTarget(x, y, z, instanceId, tx, ty, tz); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instanceId - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int x, int y, int z, int instanceId, int tx, int ty, int tz) - { - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz)) - { - return false; - } - return canSeeTarget(x, y, z, tx, ty, tz); - } - - /** - * @param prevX - * @param prevY - * @param prevGeoZ - * @param curX - * @param curY - * @param nswe - * @return the LOS Z value - */ - private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) - { - if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) - { - throw new RuntimeException("Multiple directions!"); - } - - if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) - { - return getNearestZ(curX, curY, prevGeoZ); - } - - return getNextHigherZ(curX, curY, prevGeoZ); - } - - /** - * Can see target. Does not check doors between. - * @param xValue the x coordinate - * @param yValue the y coordinate - * @param zValue the z coordinate - * @param txValue the target's x coordinate - * @param tyValue the target's y coordinate - * @param tzValue the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) - { - int x = xValue; - int y = yValue; - int tx = txValue; - int ty = tyValue; - - int geoX = getGeoX(x); - int geoY = getGeoY(y); - int tGeoX = getGeoX(tx); - int tGeoY = getGeoY(ty); - - int z = getNearestZ(geoX, geoY, zValue); - int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - // Fastpath. - if ((geoX == tGeoX) && (geoY == tGeoY)) - { - if (hasGeoPos(tGeoX, tGeoY)) - { - return z == tz; - } - return true; - } - - if (tz > z) - { - int tmp = tx; - tx = x; - x = tmp; - - tmp = ty; - ty = y; - y = tmp; - - tmp = tz; - tz = z; - z = tmp; - - tmp = tGeoX; - tGeoX = geoX; - geoX = tmp; - - tmp = tGeoY; - tGeoY = geoY; - geoY = tmp; - } - - final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); - // First point is guaranteed to be available, skip it, we can always see our own position. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - final int prevZ = pointIter.z(); - int prevGeoZ = prevZ; - int ptIndex = 0; - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - - if ((curX == prevX) && (curY == prevY)) + // Find proper size of buffer. + if (holder._size < size) { continue; } - final int beeCurZ = pointIter.z(); - int curGeoZ = prevGeoZ; - - // Check if the position has geodata. - if (hasGeoPos(curX, curY)) + // Find unlocked NodeBuffer. + for (NodeBuffer buffer : holder._buffer) { - final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); - curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); - int maxHeight; - if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) + if (!buffer.isLocked()) { - maxHeight = z + MAX_SEE_OVER_HEIGHT; - } - else - { - maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; + continue; } - boolean canSeeThrough = false; - if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) + return buffer; + } + + LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); + + // NodeBuffer not found, allocate temporary buffer. + current = new NodeBuffer(holder._size); + current.isLocked(); + } + + return current; + } + + /** + * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + * @return boolean : True, when geodata file was loaded without problem. + */ + private boolean loadGeoBlocks(int regionX, int regionY) + { + final String filename = String.format(Config.GEODATA_TYPE.getFilename(), regionX, regionY); + final String filepath = Config.GEODATA_PATH + "\\" + filename; + + // Standard load. + try (RandomAccessFile raf = new RandomAccessFile(filepath, "r"); + FileChannel fc = raf.getChannel()) + { + // Initialize file buffer. + MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // Load 18B header for L2OFF geodata (1st and 2nd byte...region X and Y). + if (Config.GEODATA_TYPE == GeoType.L2OFF) + { + for (int i = 0; i < 18; i++) { - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) + buffer.get(); + } + } + + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Loop over region blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + if (Config.GEODATA_TYPE == GeoType.L2J) { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) - { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); + // Get block type. + final byte type = buffer.get(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + case GeoStructure.TYPE_MULTILAYER_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + default: + { + throw new IllegalArgumentException("Unknown block type: " + type); + } + } } else { - canSeeThrough = true; + // Get block type. + final short type = buffer.getShort(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + default: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + } } } + } + + // Check data consistency. + if (buffer.remaining() > 0) + { + LOGGER.warning("Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); + } + + // Loading was successful. + return true; + } + catch (Exception e) + { + // An error occured while loading, load null blocks. + LOGGER.warning("Error loading " + filename + " region file. " + e); + + // Replace whole region file with null blocks. + loadNullBlocks(regionX, regionY); + + // Loading was not successful. + return false; + } + } + + /** + * Loads null blocks. Used when no region file is detected or an error occurs during loading. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + */ + private void loadNullBlocks(int regionX, int regionY) + { + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Load all null blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + _blocks[blockX + ix][blockY + iy] = _nullBlock; + } + } + } + + /** + * Converts world X to geodata X. + * @param worldX + * @return int : Geo X + */ + public static int getGeoX(int worldX) + { + return (worldX - World.WORLD_X_MIN) >> 4; + } + + /** + * Converts world Y to geodata Y. + * @param worldY + * @return int : Geo Y + */ + public static int getGeoY(int worldY) + { + return (worldY - World.WORLD_Y_MIN) >> 4; + } + + /** + * Converts geodata X to world X. + * @param geoX + * @return int : World X + */ + public static int getWorldX(int geoX) + { + return (geoX << 4) + World.WORLD_X_MIN + 8; + } + + /** + * Converts geodata Y to world Y. + * @param geoY + * @return int : World Y + */ + public static int getWorldY(int geoY) + { + return (geoY << 4) + World.WORLD_Y_MIN + 8; + } + + /** + * Returns block of geodata on given coordinates. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return {@link ABlock} : Block of geodata. + */ + public ABlock getBlock(int geoX, int geoY) + { + final int x = geoX / GeoStructure.BLOCK_CELLS_X; + if ((x < 0) || (x >= GeoStructure.GEO_BLOCKS_X)) + { + return null; + } + final int y = geoY / GeoStructure.BLOCK_CELLS_Y; + if ((y < 0) || (y >= GeoStructure.GEO_BLOCKS_Y)) + { + return null; + } + return _blocks[x][y]; + } + + /** + * Check if geo coordinates has geo. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return boolean : True, if given geo coordinates have geodata + */ + public boolean hasGeoPos(int geoX, int geoY) + { + final ABlock block = getBlock(geoX, geoY); + return (block != null) && block.hasGeoPos(); + } + + /** + * Returns the height of cell, which is closest to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell geodata Z coordinate, closest to given coordinates. + */ + public short getHeightNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return (short) worldZ; + } + return block.getHeightNearest(geoX, geoY, worldZ); + } + + /** + * Returns the NSWE flag byte of cell, which is closes to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. + */ + public byte getNsweNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return GeoStructure.CELL_FLAG_ALL; + } + return block.getNsweNearest(geoX, geoY, worldZ); + } + + /** + * Check if world coordinates has geo. + * @param worldX : World X + * @param worldY : World Y + * @return boolean : True, if given world coordinates have geodata + */ + public boolean hasGeo(int worldX, int worldY) + { + return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param loc : The location used as reference. + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(Location loc) + { + return getHeightNearest(getGeoX(loc.getX()), getGeoY(loc.getY()), loc.getZ()); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param worldX : world x + * @param worldY : world y + * @param worldZ : world z + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(int worldX, int worldY, int worldZ) + { + return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); + } + + /** + * Check line of sight from {@link WorldObject} to {@link WorldObject}.
+ * @param object : The origin object. + * @param target : The target object. + * @return True, when object can see target. + */ + public boolean canSeeTarget(WorldObject object, WorldObject target) + { + // Can always see doors. + if (target.isDoor()) + { + return true; + } + + if (object.getInstanceId() != target.getInstanceId()) + { + return false; + } + + if (DoorData.getInstance().checkIfDoorsBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceId())) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceId())) + { + return false; + } + + // Get object's and target's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getTemplate().getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + double theight = 0; + if (target instanceof Creature) + { + theight += (((Creature) target).getTemplate().getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + return canSee(object.getX(), object.getY(), object.getZ(), oheight, target.getX(), target.getY(), target.getZ(), theight) && canSee(target.getX(), target.getY(), target.getZ(), theight, object.getX(), object.getY(), object.getZ(), oheight); + } + + /** + * Check line of sight from {@link WorldObject} to {@link Location}.
+ * Note: The check uses {@link Location}'s real Z coordinate (e.g. point above ground), not its geodata representation. + * @param object : The origin object. + * @param position : The target position. + * @return True, when object can see position. + */ + public boolean canSeeLocation(WorldObject object, Location position) + { + // Get object and location coordinates. + int ox = object.getX(); + int oy = object.getY(); + int oz = object.getZ(); + int tx = position.getX(); + int ty = position.getY(); + int tz = position.getZ(); + + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, object.getInstanceId())) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, object.getInstanceId())) + { + return false; + } + + // Get object's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getTemplate().getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + // Perform geodata check. + return canSee(ox, oy, oz, oheight, tx, ty, tz, 0) && canSee(tx, ty, tz, 0, ox, oy, oz, oheight); + } + + /** + * Simple check for origin to target visibility.
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param oheight : The height of origin, used as start point. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param theight : The height of target, used as end point. + * @return True, when origin can see target. + */ + public boolean canSee(int ox, int oy, int oz, double oheight, int tx, int ty, int tz, double theight) + { + // Get origin geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get target geodata coordinates. + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check being on same cell and layer (index). + // Note: Get index must use origin height increased by cell height, the method returns index to height exclusive self. + int index = block.getIndexBelow(gox, goy, oz + GeoStructure.CELL_HEIGHT); + if (index < 0) + { + return false; + } + + if ((gox == gtx) && (goy == gty)) + { + return index == block.getIndexBelow(gtx, gty, tz + GeoStructure.CELL_HEIGHT); + } + + // Get ground and nswe flag. + int groundZ = block.getHeight(index); + int nswe = block.getNswe(index); + + // Get delta coordinates, slope of line (XY, XZ) and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double dz = (tz + theight) - (oz + oheight); + final double m = (double) dy / dx; + final double mz = dz / Math.sqrt((dx * dx) + (dy * dy)); + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); + + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) + { + // Set next cell in X direction. + gridX += mdt.getStepX(); + gox += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = Util.limit(checkX, gridX, gridX + 15); - if (!canSeeThrough) - { - return false; - } + // Set next cell in Y direction. + gridY += mdt.getStepY(); + goy += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevGeoZ = curGeoZ; - ++ptIndex; + // Get block of the next cell. + block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get line of sight height (including Z slope). + double losz = oz + oheight + Config.MAX_OBSTACLE_HEIGHT; + losz += mz * Math.sqrt(((checkX - ox) * (checkX - ox)) + ((checkY - oy) * (checkY - oy))); + + // Check line of sight going though wall (vertical check). + + // Get index of particular layer, based on last iterated cell conditions. + boolean canMove = (nswe & dir) != 0; + if (canMove) + { + // No wall present, get next cell below current cell. + index = block.getIndexBelow(gox, goy, groundZ + GeoStructure.CELL_IGNORE_HEIGHT); + } + else + { + // Wall present, get next cell above current cell. + index = block.getIndexAbove(gox, goy, groundZ - (2 * GeoStructure.CELL_HEIGHT)); + } + + // Next cell's does not exist (no geodata with valid condition), return fail. + if (index < 0) + { + return false; + } + + // Get next cell's layer height. + int z = block.getHeight(index); + + // Perform sine of sight check (next cell is above line of sight line), return fail. + if (z > losz) + { + return false; + } + + // Next cell is accessible, update z and NSWE. + groundZ = z; + nswe = block.getNswe(index); } + // Iteration is completed, no obstacle is found. return true; } /** - * Move check. - * @param x the x coordinate - * @param y the y coordinate - * @param zValue the z coordinate - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tzValue the target's z coordinate - * @param instanceId the instance id - * @return the last Location (x,y,z) where player can walk - just before wall + * Check movement from coordinates to coordinates.
+ * Note: The Z coordinates are supposed to be already validated geodata coordinates. + * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instanceId + * @return True, when target coordinates are reachable from origin coordinates. */ - public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, int instanceId) + public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) { - final int geoX = getGeoX(x); - final int geoY = getGeoY(y); - final int z = getNearestZ(geoX, geoY, zValue); - final int tGeoX = getGeoX(tx); - final int tGeoY = getGeoY(ty); - final int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instanceId)) { - return new Location(x, y, getHeight(x, y, z)); - } - if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz)) - { - return new Location(x, y, getHeight(x, y, z)); + return false; } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = z; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instanceId)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return false; + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + int goz = getHeightNearest(gox, goy, oz); + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check movement within same cell. + if ((gox == gtx) && (goy == gty)) + { + return goz == getHeight(tx, ty, tz); + } + + // Get nswe flag. + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - // Can't move, return previous location. - return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = Util.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check point heading into obstacle, if so return current point. + if ((nswe & dir) == 0) + { + return false; + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return false; + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != tz)) - { - // Different floors, return start location. - return new Location(x, y, z); - } - - return new Location(tx, ty, tz); + // When origin Z is target Z, the move is successful. + return goz == getHeight(tx, ty, tz); } /** - * Checks if its possible to move from one location to another. - * @param fromX the X coordinate to start checking from - * @param fromY the Y coordinate to start checking from - * @param fromZvalue the Z coordinate to start checking from - * @param toX the X coordinate to end checking at - * @param toY the Y coordinate to end checking at - * @param toZvalue the Z coordinate to end checking at - * @param instanceId the instance - * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + * Check movement from origin to target coordinates. Returns last available point in the checked path.
+ * Target X and Y reachable and Z is on same floor: + *
    + *
  • Location of the target with corrected Z value from geodata.
  • + *
+ * Target X and Y reachable but Z is on another floor: + *
    + *
  • Location of the origin with corrected Z value from geodata.
  • + *
+ * Target X and Y not reachable: + *
    + *
  • Last accessible location in destination to target.
  • + *
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instanceId + * @return The {@link Location} representing last point of movement (e.g. just before wall). */ - public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, int instanceId) + public Location getValidLocation(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) { - final int geoX = getGeoX(fromX); - final int geoY = getGeoY(fromY); - final int fromZ = getNearestZ(geoX, geoY, fromZvalue); - final int tGeoX = getGeoX(toX); - final int tGeoY = getGeoY(toY); - final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); - - if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instanceId)) { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ)) - { - return false; + return new Location(ox, oy, oz); } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = fromZ; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instanceId)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return new Location(ox, oy, oz); + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + final int gtz = getHeightNearest(gtx, gty, tz); + int goz = getHeightNearest(gox, goy, oz); + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - return false; - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = Util.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check target cell is out of geodata grid (world coordinates). + if ((nx < 0) || (nx >= GeoStructure.GEO_CELLS_X) || (ny < 0) || (ny >= GeoStructure.GEO_CELLS_Y)) + { + return new Location(checkX, checkY, goz); + } + + // Check point heading into obstacle, if so return current (border) point. + if ((nswe & dir) == 0) + { + return new Location(checkX, checkY, goz); + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current (border) point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return new Location(checkX, checkY, goz); + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) - { - // Different floors. - return false; - } - - return true; + // Compare Z coordinates: + // If same, path is okay, return target point and fix its Z geodata coordinate. + // If not same, path is does not exist, return origin point. + return goz == gtz ? new Location(tx, ty, gtz) : new Location(ox, oy, oz); } + /** + * Returns the list of location objects as a result of complete path calculation. + * @param ox : origin x + * @param oy : origin y + * @param oz : origin z + * @param tx : target x + * @param ty : target y + * @param tz : target z + * @param instanceId + * @return {@code List} : complete path from nodes + */ + public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) + { + // Get origin and check existing geo coords. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + if (!hasGeoPos(gox, goy)) + { + return Collections.emptyList(); + } + + int goz = getHeightNearest(gox, goy, oz); + + // Get target and check existing geo coords. + int gtx = getGeoX(tx); + int gty = getGeoY(ty); + if (!hasGeoPos(gtx, gty)) + { + return Collections.emptyList(); + } + + int gtz = getHeightNearest(gtx, gty, tz); + + // Prepare buffer for pathfinding calculations. + NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + if (buffer == null) + { + return Collections.emptyList(); + } + + // Find path. + List path = null; + try + { + path = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + if (path.isEmpty()) + { + return Collections.emptyList(); + } + } + catch (Exception e) + { + return Collections.emptyList(); + } + finally + { + buffer.free(); + } + + // Check path. + if (path.size() < 3) + { + return path; + } + + // Get path list iterator. + ListIterator point = path.listIterator(); + + // Get node A (origin). + int nodeAx = ox; + int nodeAy = oy; + int nodeAz = goz; + + // Get node B. + Location nodeB = point.next(); + + // Iterate thought the path to optimize it. + while (point.hasNext()) + { + // Get node C. + Location nodeC = path.get(point.nextIndex()); + + // Check movement from node A to node C. + if (canMoveToTarget(nodeAx, nodeAy, nodeAz, nodeC.getX(), nodeC.getY(), nodeC.getZ(), instanceId)) + { + // Can move from node A to node C. + // Remove node B. + point.remove(); + } + else + { + // Can not move from node A to node C. + // Set node A (node B is part of path, update A coordinates). + nodeAx = nodeB.getX(); + nodeAy = nodeB.getY(); + nodeAz = nodeB.getZ(); + } + + // Set node B. + nodeB = point.next(); + } + + return path; + } + + /** + * NodeBuffer container with specified size and count of separate buffers. + */ + private static class BufferHolder + { + final int _size; + final List _buffer; + + public BufferHolder(int size, int count) + { + _size = size; + _buffer = new ArrayList<>(count); + + for (int i = 0; i < count; i++) + { + _buffer.add(new NodeBuffer(size)); + } + } + } + + /** + * Returns the instance of the {@link GeoEngine}. + * @return {@link GeoEngine} : The instance. + */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -735,4 +1053,4 @@ public class GeoEngine { protected static final GeoEngine INSTANCE = new GeoEngine(); } -} +} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java deleted file mode 100644 index 43ac4d89b8..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ /dev/null @@ -1,300 +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 . - */ -package org.l2jmobius.gameserver.geoengine; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; -import org.l2jmobius.gameserver.model.World; - -/** - * @author -Nemesiss- - */ -public class GeoEnginePathfinding -{ - private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); - - private BufferInfo[] _buffers; - - protected GeoEnginePathfinding() - { - try - { - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - - _buffers = 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); - } - - _buffers[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"); - } - } - - public boolean pathNodesExist(short regionoffset) - { - return false; - } - - public List findPath(int x, int y, int z, int tx, int ty, int tz, int instanceId) - { - 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)))); - if (buffer == null) - { - return null; - } - - List path = null; - try - { - final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); - - if (result == null) - { - return null; - } - - path = constructPath(result); - } - catch (Exception e) - { - LOGGER.warning(e.getMessage()); - return null; - } - finally - { - buffer.free(); - } - - // check path - if (path.size() < 3) - { - return path; - } - - int currentX, currentY, currentZ; - ListIterator middlePoint; - - middlePoint = path.listIterator(); - currentX = x; - currentY = y; - currentZ = z; - - while (middlePoint.hasNext()) - { - final AbstractNodeLoc locMiddle = middlePoint.next(); - if (!middlePoint.hasNext()) - { - break; - } - - final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); - if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instanceId)) - { - middlePoint.remove(); - } - else - { - currentX = locMiddle.getX(); - currentY = locMiddle.getY(); - currentZ = locMiddle.getZ(); - } - } - - return path; - } - - /** - * Convert geodata position to pathnode position - * @param geo_pos - * @return pathnode position - */ - public short getNodePos(int geo_pos) - { - return (short) (geo_pos >> 3); // OK? - } - - /** - * Convert node position to pathnode block position - * @param node_pos - * @return pathnode block position (0...255) - */ - public short getNodeBlock(int node_pos) - { - return (short) (node_pos % 256); - } - - public byte getRegionX(int node_pos) - { - return (byte) ((node_pos >> 8) + World.TILE_X_MIN); - } - - public byte getRegionY(int node_pos) - { - return (byte) ((node_pos >> 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 node_x rx - * @return - */ - public int calculateWorldX(short node_x) - { - return World.MAP_MIN_X + (node_x * 128) + 48; - } - - /** - * Convert pathnode y to World y position - * @param node_y - * @return - */ - public int calculateWorldY(short node_y) - { - return World.MAP_MIN_Y + (node_y * 128) + 48; - } - - private List constructPath(AbstractNode nodeValue) - { - final LinkedList path = new LinkedList<>(); - int previousDirectionX = Integer.MIN_VALUE; - int previousDirectionY = Integer.MIN_VALUE; - int directionX, directionY; - - AbstractNode node = nodeValue; - while (node.getParent() != null) - { - directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); - directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); - - // only add a new route point if moving direction changes - if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) - { - previousDirectionX = directionX; - previousDirectionY = directionY; - - path.addFirst(node.getLoc()); - node.setLoc(null); - } - - node = node.getParent(); - } - - return path; - } - - private CellNodeBuffer alloc(int size) - { - CellNodeBuffer current = null; - for (BufferInfo i : _buffers) - { - if (i.mapSize >= size) - { - for (CellNodeBuffer buf : i.buffer) - { - if (buf.lock()) - { - current = buf; - break; - } - } - if (current != null) - { - break; - } - - // not found, allocate temporary buffer - current = new CellNodeBuffer(i.mapSize); - current.lock(); - if (i.buffer.size() < i.count) - { - i.buffer.add(current); - break; - } - } - } - - return current; - } - - private static final class BufferInfo - { - final int mapSize; - final int count; - ArrayList buffer; - - public BufferInfo(int size, int cnt) - { - mapSize = size; - count = cnt; - buffer = new ArrayList<>(count); - } - } - - public static GeoEnginePathfinding getInstance() - { - return SingletonHolder.INSTANCE; - } - - private static class SingletonHolder - { - protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); - } -} diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java new file mode 100644 index 0000000000..49ec35aa1c --- /dev/null +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java @@ -0,0 +1,85 @@ +/* + * 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 . + */ +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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @param index : Index of the cell. + * @return short : Cell geodata Z coordinate, below given coordinates. + */ + public abstract byte getNswe(int index); +} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java new file mode 100644 index 0000000000..f5997bf287 --- /dev/null +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java @@ -0,0 +1,130 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java new file mode 100644 index 0000000000..9af9d2a28a --- /dev/null +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java @@ -0,0 +1,95 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java new file mode 100644 index 0000000000..31fbf5d477 --- /dev/null +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java new file mode 100644 index 0000000000..f77d21d84b --- /dev/null +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java @@ -0,0 +1,68 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +public class BlockNull extends ABlock +{ + @Override + public boolean hasGeoPos() + { + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java deleted file mode 100644 index 61ea4fcf82..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java +++ /dev/null @@ -1,48 +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 . - */ -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() - { - } -} diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java deleted file mode 100644 index 3c8de1a7f7..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java +++ /dev/null @@ -1,77 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java deleted file mode 100644 index 1ccdefe3da..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java +++ /dev/null @@ -1,56 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java new file mode 100644 index 0000000000..691b164e0c --- /dev/null +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ +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_ALL = 0x0F; + + // 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; +} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java deleted file mode 100644 index 25eafc5a04..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java +++ /dev/null @@ -1,42 +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 . - */ -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); -} diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java deleted file mode 100644 index 0d2c765002..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java +++ /dev/null @@ -1,47 +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 . - */ -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(); -} diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java deleted file mode 100644 index 52df457360..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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); - - // 1 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; - } -} diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java deleted file mode 100644 index 72a5a635c7..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java +++ /dev/null @@ -1,55 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author HorridoJoho - */ -public final class NullRegion implements IRegion -{ - public static final NullRegion INSTANCE = new NullRegion(); - - @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 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; - } -} diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java deleted file mode 100644 index fc924cb875..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java +++ /dev/null @@ -1,92 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java deleted file mode 100644 index 5087513ed9..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java +++ /dev/null @@ -1,87 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public abstract class AbstractNode -{ - private T _loc; - private AbstractNode _parent; - - public AbstractNode(T loc) - { - _loc = loc; - } - - public void setParent(AbstractNode p) - { - _parent = p; - } - - public AbstractNode getParent() - { - return _parent; - } - - public T getLoc() - { - return _loc; - } - - public void setLoc(T l) - { - _loc = l; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); - return result; - } - - @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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java deleted file mode 100644 index 7223b5b2be..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java +++ /dev/null @@ -1,33 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -/** - * @author -Nemesiss- - */ -public abstract class AbstractNodeLoc -{ - public abstract int getX(); - - public abstract int getY(); - - public abstract int getZ(); - - public abstract int getNodeX(); - - public abstract int getNodeY(); -} diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java deleted file mode 100644 index 3425234e0e..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public class CellNode extends AbstractNode -{ - 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java deleted file mode 100644 index 522610bb7c..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java +++ /dev/null @@ -1,348 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -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 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) - { - _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(); - } - - public final List debugPath() - { - final List 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 void getNeighbors() - { - if (!_current.getLoc().canGoAll()) - { - 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); - } - - // SouthEast - if ((nodeE != null) && (nodeS != null)) - { - if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) - { - addNode(x + 1, y + 1, z, true); - } - } - - // SouthWest - if ((nodeS != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) - { - addNode(x - 1, y + 1, z, true); - } - } - - // NorthEast - if ((nodeN != null) && (nodeE != null)) - { - if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) - { - addNode(x + 1, y - 1, z, true); - } - } - - // NorthWest - if ((nodeN != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) - { - addNode(x - 1, y - 1, z, true); - } - } - } - - private 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(); - // reinit node if needed - if (result.getLoc() != null) - { - result.getLoc().set(x, y, z); - } - else - { - result.setLoc(new NodeLoc(x, y, z)); - } - } - - return result; - } - - private 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 boolean isHighWeight(int x, int y, int z) - { - final CellNode result = getNode(x, y, z); - if (result == null) - { - return true; - } - - if (!result.getLoc().canGoAll()) - { - return true; - } - if (Math.abs(result.getLoc().getZ() - z) > 16) - { - return true; - } - - return false; - } - - private 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java new file mode 100644 index 0000000000..fa5ca43c2f --- /dev/null +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -0,0 +1,111 @@ +/* + * 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 . + */ +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 geodata values. + private int _geoX; + private int _geoY; + private byte _nswe; + + // 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; + + _costG = 0; + _costH = 0; + _costF = 0; + + _parent = null; + } + + public void setGeo(int gx, int gy, int gz, byte nswe) + { + super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); + + _geoX = gx; + _geoY = gy; + _nswe = nswe; + } + + 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 int getCostF() + { + return _costF; + } + + public Node getParent() + { + return _parent; + } + + @Override + public int compareTo(Node o) + { + return _costF - o._costF; + } +} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java new file mode 100644 index 0000000000..b464212c96 --- /dev/null +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -0,0 +1,368 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.awt.Color; +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; +import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; + +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 _opened; + // Container holding Nodes already explored. + private final List _closed; + + // Target coordinates. + private int _gtx; + private int _gty; + private int _gtz; + + // Pathfinding statistics. + private long _timeStamp; + private long _lastElapsedTime; + + 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 findPath(int gox, int goy, int goz, int gtx, int gty, int gtz) + { + // Set start timestamp. + _timeStamp = System.currentTimeMillis(); + + // 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)); + _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 constructPath() + { + // Create result. + final LinkedList 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; + } + + /** + * Creates list of Nodes to show debug path. + * @param debug : The debug packet to add debug informations in. + */ + public void debugPath(ExServerPrimitive debug) + { + // Add all opened node as yellow points. + for (Node n : _opened) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); + } + + // Add all opened node as blue points. + for (Node n : _closed) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); + } + } + + 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; + + _lastElapsedTime = System.currentTimeMillis() - _timeStamp; + _lock.unlock(); + } + + public long getElapsedTime() + { + return _lastElapsedTime; + } + + /** + * Expand the current {@link Node} by exploring its neighbors (axially and diagonally). + */ + private void expand() + { + // Movement is blocked, skip. + final byte nswe = _current.getNSWE(); + if (nswe == 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; + + // Can move north, expand. + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + } + + // Can move south, expand. + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + } + + // Can move west, expand. + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move east, expand. + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move north-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move north-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + } + + /** + * 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 weight : The weight of movement to the new node. + * @return The nswe of the added node. Blank, if not added. + */ + private byte addNode(int gx, int gy, int gzValue, int weight) + { + // 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]; + + // Set node geodata coordinates. + node.setGeo(gx, gy, gz, nswe); + + // 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, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + _opened.add(node); + _bufferIndex++; + return nswe; + } + + /** + * Calculate cost H value, calculated using diagonal distance method.
+ * 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. + final int dx = Math.abs(gx - _gtx); + final int dy = Math.abs(gy - _gty); + final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + + // Get diagonal and axial differences to the target. + final int dd = Math.min(dx, dy); + final int da = Math.max(dx, dy) - dd; + + // Calculate the diagonal distance of the node to the target. + return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + } +} \ No newline at end of file diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java deleted file mode 100644 index e3bad04b25..0000000000 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; - -/** - * @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 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 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; - // return super.hashCode(); - } - - @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; - if (_x != other._x) - { - return false; - } - if (_y != other._y) - { - return false; - } - if (_goNorth != other._goNorth) - { - return false; - } - if (_goEast != other._goEast) - { - return false; - } - if (_goSouth != other._goSouth) - { - return false; - } - if (_goWest != other._goWest) - { - return false; - } - if (_geoHeight != other._geoHeight) - { - return false; - } - return true; - } -} diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeodata.java index 893d3ef03a..16863403df 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeodata.java @@ -47,8 +47,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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); @@ -63,8 +63,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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); @@ -118,8 +118,8 @@ public class AdminGeodata implements IAdminCommandHandler } else if (command.equals("admin_geomap")) { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y); } else diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminZone.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminZone.java index d392f69966..6ea61d0142 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminZone.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminZone.java @@ -88,8 +88,8 @@ public class AdminZone implements IAdminCommandHandler { final int x = activeChar.getX(); final int y = activeChar.getY(); - final int rx = ((x - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int ry = ((y - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int rx = ((x - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int ry = ((y - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final NpcHtmlMessage html = new NpcHtmlMessage(0); html.setFile("data/html/admin/zone.htm"); diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/handler/itemhandlers/RollingDice.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/handler/itemhandlers/RollingDice.java index 144c31a3db..5982af7701 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/handler/itemhandlers/RollingDice.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/handler/itemhandlers/RollingDice.java @@ -82,7 +82,7 @@ public class RollingDice implements IItemHandler final int x = player.getX() + x1; final int y = player.getY() + y1; final int z = player.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceId()); + final Location destination = GeoEngine.getInstance().getValidLocation(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceId()); Broadcast.toSelfAndKnownPlayers(player, new Dice(player.getObjectId(), item.getItemId(), number, destination.getX(), destination.getY(), destination.getZ())); final SystemMessage sm = new SystemMessage(SystemMessageId.S1_HAS_ROLLED_S2); diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/handler/skillhandlers/Fishing.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/handler/skillhandlers/Fishing.java index 250a6009eb..1e6c48b89d 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/handler/skillhandlers/Fishing.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/handler/skillhandlers/Fishing.java @@ -134,11 +134,11 @@ public class Fishing implements ISkillHandler if ((water != null)) { final Location waterLocation = new Location(x, y, water.getWaterZ() - 50); - if ((aimingTo != null) && GeoEngine.getInstance().canSeeTarget(player, waterLocation)) + if ((aimingTo != null) && GeoEngine.getInstance().canSeeLocation(player, waterLocation)) { z = water.getWaterZ() + 10; } - else if ((aimingTo != null) && GeoEngine.getInstance().canSeeTarget(player, waterLocation)) + else if ((aimingTo != null) && GeoEngine.getInstance().canSeeLocation(player, waterLocation)) { z = aimingTo.getWaterZ() + 10; } diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/Location.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/Location.java index 47181d1a9e..71dba4e137 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/Location.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/Location.java @@ -16,87 +16,88 @@ */ package org.l2jmobius.gameserver.model; -import org.l2jmobius.gameserver.model.actor.Creature; +import java.util.Objects; -public class Location +import org.l2jmobius.commons.util.Point2D; + +/** + * A datatype used to retain a 3D (x/y/z/heading) point. It got the capability to be set and cleaned. + */ +public class Location extends Point2D { public static final Location DUMMY_LOC = new Location(0, 0, 0); - protected int _x; - protected int _y; - protected int _z; - private int _heading = 0; + protected volatile int _z; + protected volatile int _heading; public Location(int x, int y, int z) { - _x = x; - _y = y; + super(x, y); _z = z; + _heading = 0; } public Location(int x, int y, int z, int heading) { - _x = x; - _y = y; + super(x, y); _z = z; _heading = heading; } public Location(WorldObject obj) { - _x = obj.getX(); - _y = obj.getY(); - _z = obj.getZ(); + this(obj.getX(), obj.getY(), obj.getZ(), obj.getPosition().getHeading(), obj.getInstanceId()); } - public Location(Creature obj) + public Location(int x, int y, int z, int heading, int instanceId) { - _x = obj.getX(); - _y = obj.getY(); - _z = obj.getZ(); - _heading = obj.getHeading(); + super(x, y); + _z = z; + _heading = heading; } + public Location(StatSet set) + { + super(set.getInt("x", 0), set.getInt("y", 0)); + _z = set.getInt("z", 0); + _heading = set.getInt("heading", 0); + } + + /** + * Get the x coordinate. + * @return the x coordinate + */ + @Override public int getX() { return _x; } + /** + * Get the y coordinate. + * @return the y coordinate + */ + @Override public int getY() { return _y; } + /** + * Get the z coordinate. + * @return the z coordinate + */ public int getZ() { return _z; } - public int getHeading() - { - return _heading; - } - - public void setX(int x) - { - _x = x; - } - - public void setY(int y) - { - _y = y; - } - - public void setZ(int z) - { - _z = z; - } - - public void setHeading(int head) - { - _heading = head; - } - + /** + * Set the x, y, z coordinates. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + */ public void setXYZ(int x, int y, int z) { _x = x; @@ -104,8 +105,85 @@ public class Location _z = z; } + /** + * Set the x, y, z coordinates. + * @param loc The location. + */ + public void setXYZ(Location loc) + { + setXYZ(loc.getX(), loc.getY(), loc.getZ()); + } + + /** + * Get the heading. + * @return the heading + */ + public int getHeading() + { + return _heading; + } + + /** + * Set the heading. + * @param heading the heading + */ + public void setHeading(int heading) + { + _heading = heading; + } + + public void setLocation(Location loc) + { + _x = loc.getX(); + _y = loc.getY(); + _z = loc.getZ(); + _heading = loc.getHeading(); + } + + @Override + public void clean() + { + super.clean(); + _z = 0; + } + + @Override + public Location clone() + { + return new Location(_x, _y, _z); + } + + @Override + public int hashCode() + { + return (31 * super.hashCode()) + Objects.hash(_z); + } + + /** + * @param x : The X coord to test. + * @param y : The Y coord to test. + * @param z : The X coord to test. + * @return True if all coordinates equals this {@link Point2D} coordinates. + */ public boolean equals(int x, int y, int z) { return (_x == x) && (_y == y) && (_z == z); } + + @Override + public boolean equals(Object obj) + { + if (obj instanceof Location) + { + final Location loc = (Location) obj; + return (getX() == loc.getX()) && (getY() == loc.getY()) && (getZ() == loc.getZ()) && (getHeading() == loc.getHeading()); + } + return false; + } + + @Override + public String toString() + { + return "[" + getClass().getSimpleName() + "] X: " + _x + " Y: " + _y + " Z: " + _z + " Heading: " + _heading; + } } diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/ObjectPosition.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/ObjectPosition.java index 889bb04153..e8e841c1a7 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/ObjectPosition.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/ObjectPosition.java @@ -99,23 +99,23 @@ public class ObjectPosition public void setXYZInvisible(int x, int y, int z) { int correctX = x; - if (correctX > World.MAP_MAX_X) + if (correctX > World.WORLD_X_MAX) { - correctX = World.MAP_MAX_X - 5000; + correctX = World.WORLD_X_MAX - 5000; } - if (correctX < World.MAP_MIN_X) + if (correctX < World.WORLD_X_MIN) { - correctX = World.MAP_MIN_X + 5000; + correctX = World.WORLD_X_MIN + 5000; } int correctY = y; - if (correctY > World.MAP_MAX_Y) + if (correctY > World.WORLD_Y_MAX) { - correctY = World.MAP_MAX_Y - 5000; + correctY = World.WORLD_Y_MAX - 5000; } - if (correctY < World.MAP_MIN_Y) + if (correctY < World.WORLD_Y_MIN) { - correctY = World.MAP_MIN_Y + 5000; + correctY = World.WORLD_Y_MIN + 5000; } setWorldPosition(correctX, correctY, z); @@ -180,15 +180,6 @@ public class ObjectPosition return getWorldPosition().getX(); } - /** - * Sets the x. - * @param value the new x - */ - public void setX(int value) - { - getWorldPosition().setX(value); - } - /** * Return the y position of the WorldObject. * @return the y @@ -198,15 +189,6 @@ public class ObjectPosition return getWorldPosition().getY(); } - /** - * Sets the y. - * @param value the new y - */ - public void setY(int value) - { - getWorldPosition().setY(value); - } - /** * Return the z position of the WorldObject. * @return the z @@ -216,15 +198,6 @@ public class ObjectPosition return getWorldPosition().getZ(); } - /** - * Sets the z. - * @param value the new z - */ - public void setZ(int value) - { - getWorldPosition().setZ(value); - } - /** * Gets the world position. * @return the world position diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/World.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/World.java index 6a55e72a68..bd84cc1b8f 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/World.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/World.java @@ -28,9 +28,6 @@ import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.actor.instance.PetInstance; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -/** - * @version $Revision: 1.21.2.5.2.7 $ $Date: 2005/03/27 15:29:32 $ - */ public class World { private static final Logger LOGGER = Logger.getLogger(World.class.getName()); @@ -38,32 +35,31 @@ public class World public static volatile int MAX_CONNECTED_COUNT = 0; public static volatile int OFFLINE_TRADE_COUNT = 0; - public static final int SHIFT_BY = 12; + /** Bit shift, defines number of regions note, shifting by 15 will result in regions corresponding to map tiles shifting by 11 divides one tile to 16x16 regions. */ + public static final int SHIFT_BY = 11; - // Geodata min/max tiles - public static final int TILE_X_MIN = 16; - public static final int TILE_X_MAX = 26; - public static final int TILE_Y_MIN = 10; - public static final int TILE_Y_MAX = 25; - - // Map dimensions public static final int TILE_SIZE = 32768; - public static final int MAP_MIN_X = (TILE_X_MIN - 20) * TILE_SIZE; - public static final int MAP_MAX_X = (TILE_X_MAX - 19) * TILE_SIZE; - public static final int MAP_MIN_Y = (TILE_Y_MIN - 18) * TILE_SIZE; - public static final int MAP_MAX_Y = (TILE_Y_MAX - 17) * TILE_SIZE; - /** calculated offset used so top left region is 0,0. */ - public static final int OFFSET_X = Math.abs(MAP_MIN_X >> SHIFT_BY); + /** Map dimensions. */ + public static final int TILE_X_MIN = 16; + public static final int TILE_Y_MIN = 10; + public static final int TILE_X_MAX = 26; + public static final int TILE_Y_MAX = 25; + public static final int TILE_ZERO_COORD_X = 20; + public static final int TILE_ZERO_COORD_Y = 18; + public static final int WORLD_X_MIN = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; + public static final int WORLD_Y_MIN = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; - /** The Constant OFFSET_Y. */ - public static final int OFFSET_Y = Math.abs(MAP_MIN_Y >> SHIFT_BY); + public static final int WORLD_X_MAX = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; + public static final int WORLD_Y_MAX = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; - /** number of regions. */ - private static final int REGIONS_X = (MAP_MAX_X >> SHIFT_BY) + OFFSET_X; + /** Calculated offset used so top left region is 0,0 */ + public static final int OFFSET_X = Math.abs(WORLD_X_MIN >> SHIFT_BY); + public static final int OFFSET_Y = Math.abs(WORLD_Y_MIN >> SHIFT_BY); - /** The Constant REGIONS_Y. */ - private static final int REGIONS_Y = (MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y; + /** Number of regions. */ + private static final int REGIONS_X = (WORLD_X_MAX >> SHIFT_BY) + OFFSET_X; + private static final int REGIONS_Y = (WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y; /** HashMap(String Player name, PlayerInstance) containing all the players in game. */ private static Map _allPlayers = new ConcurrentHashMap<>(); diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/WorldObject.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/WorldObject.java index c47018812d..42fc3feceb 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/WorldObject.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/WorldObject.java @@ -242,23 +242,23 @@ public abstract class WorldObject _isSpawned = true; int spawnX = x; - if (spawnX > World.MAP_MAX_X) + if (spawnX > World.WORLD_X_MAX) { - spawnX = World.MAP_MAX_X - 5000; + spawnX = World.WORLD_X_MAX - 5000; } - if (spawnX < World.MAP_MIN_X) + if (spawnX < World.WORLD_X_MIN) { - spawnX = World.MAP_MIN_X + 5000; + spawnX = World.WORLD_X_MIN + 5000; } int spawnY = y; - if (spawnY > World.MAP_MAX_Y) + if (spawnY > World.WORLD_Y_MAX) { - spawnY = World.MAP_MAX_Y - 5000; + spawnY = World.WORLD_Y_MAX - 5000; } - if (spawnY < World.MAP_MIN_Y) + if (spawnY < World.WORLD_Y_MIN) { - spawnY = World.MAP_MIN_Y + 5000; + spawnY = World.WORLD_Y_MIN + 5000; } getPosition().setWorldPosition(spawnX, spawnY, z); diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/actor/Creature.java index 20ffbcce80..450b5a52b7 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -44,8 +44,6 @@ import org.l2jmobius.gameserver.data.xml.MapRegionData; import org.l2jmobius.gameserver.data.xml.ZoneData; import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.handler.ISkillHandler; import org.l2jmobius.gameserver.handler.SkillHandler; import org.l2jmobius.gameserver.handler.itemhandlers.Potions; @@ -4306,7 +4304,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder public int _heading; public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -5513,8 +5511,8 @@ public abstract class Creature extends WorldObject implements ISkillsHolder final int originalX = x; final int originalY = y; final int originalZ = z; - final int gtx = (originalX - World.MAP_MIN_X) >> 4; - final int gty = (originalY - World.MAP_MIN_Y) >> 4; + final int gtx = (originalX - World.WORLD_X_MIN) >> 4; + final int gty = (originalY - World.WORLD_Y_MIN) >> 4; if (isOnGeodataPath()) { // try @@ -5532,7 +5530,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder } // Temporary fix for character outside world region errors (should not happen) - if ((curX < World.MAP_MIN_X) || (curX > World.MAP_MAX_X) || (curY < World.MAP_MIN_Y) || (curY > World.MAP_MAX_Y)) + if ((curX < World.WORLD_X_MIN) || (curX > World.WORLD_X_MAX) || (curY < World.WORLD_Y_MIN) || (curY > World.WORLD_Y_MAX)) { LOGGER.warning("Character " + getName() + " outside world area, in coordinates x:" + curX + " y:" + curY); getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); @@ -5557,7 +5555,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder && !(((curZ - z) > 300) && (distance < 300))) // Prohibit correcting destination if character wants to fall. { // location different if destination wasn't reached (or just z coord is different) - final Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(curX, curY, curZ, x, y, z, getInstanceId()); + final Location destiny = GeoEngine.getInstance().getValidLocation(curX, curY, curZ, x, y, z, getInstanceId()); x = destiny.getX(); y = destiny.getY(); z = destiny.getZ(); @@ -5571,7 +5569,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder if (((originalDistance - distance) > 30) && !_isAfraid && !isInBoat) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId()); + m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { if (isPlayer() && !_isFlying && !isInWater) diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/actor/Summon.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/actor/Summon.java index 667d93cab6..62ed1915a8 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/actor/Summon.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/actor/Summon.java @@ -98,7 +98,7 @@ public abstract class Summon extends Playable final int x = owner.getX(); final int y = owner.getY(); final int z = owner.getZ(); - final Location location = GeoEngine.getInstance().canMoveToTargetLoc(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, getInstanceId()); + final Location location = GeoEngine.getInstance().getValidLocation(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, getInstanceId()); setXYZInvisible(location.getX(), location.getY(), location.getZ()); } diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java index 7b32ab2006..698301880e 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java @@ -1045,7 +1045,7 @@ public class ItemInstance extends WorldObject int z = zValue; if (Config.PATHFINDING && (dropper != null)) { - final Location dropDest = GeoEngine.getInstance().canMoveToTargetLoc(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, dropper.getInstanceId()); + final Location dropDest = GeoEngine.getInstance().getValidLocation(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, dropper.getInstanceId()); if ((dropDest != null) && (dropDest.getX() != 0) && (dropDest.getY() != 0)) { x = dropDest.getX(); diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/skills/effects/EffectFear.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/skills/effects/EffectFear.java index 67fc3232be..9ec4af1c17 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/skills/effects/EffectFear.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/model/skills/effects/EffectFear.java @@ -119,7 +119,7 @@ final class EffectFear extends Effect posX += signx * FEAR_RANGE; posY += signy * FEAR_RANGE; - final Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(getEffected().getX(), getEffected().getY(), getEffected().getZ(), posX, posY, posZ, getEffected().getInstanceId()); + final Location destiny = GeoEngine.getInstance().getValidLocation(getEffected().getX(), getEffected().getY(), getEffected().getZ(), posX, posY, posZ, getEffected().getInstanceId()); getEffected().setRunning(); getEffected().getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location(destiny.getX(), destiny.getY(), destiny.getZ(), 0)); return true; diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java index f33cd87164..783106861f 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java @@ -111,7 +111,7 @@ public class MoveBackwardToLocation extends GameClientPacket } // Mobius: Check for possible door logout and move over exploit. Also checked at ValidatePosition. - if (DoorData.getInstance().checkIfDoorsBetween(player.getX(), player.getY(), player.getZ(), _targetX, _targetY, _targetZ)) + if (DoorData.getInstance().checkIfDoorsBetween(player.getX(), player.getY(), player.getZ(), _targetX, _targetY, _targetZ, player.getInstanceId())) { player.stopMove(player.getLastServerPosition()); player.sendPacket(ActionFailed.STATIC_PACKET); diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java index 1d93093e3d..7f27a041f0 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java @@ -104,7 +104,7 @@ public class ValidatePosition extends GameClientPacket { if (player.isFalling(_z)) { - final int nearestZ = GeoEngine.getInstance().getHigherHeight(_x, _y, _z); + final int nearestZ = GeoEngine.getInstance().getHeight(_x, _y, _z); if (player.getZ() < nearestZ) { player.setXYZ(_x, _y, nearestZ); @@ -124,7 +124,7 @@ public class ValidatePosition extends GameClientPacket player.setClientHeading(_heading); // No real need to validate heading. // Mobius: Check for possible door logout and move over exploit. Also checked at MoveBackwardToLocation. - if (!DoorData.getInstance().checkIfDoorsBetween(realX, realY, realZ, _x, _y, _z)) + if (!DoorData.getInstance().checkIfDoorsBetween(realX, realY, realZ, _x, _y, _z, player.getInstanceId())) { player.setLastServerPosition(realX, realY, realZ); } diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/util/GeoUtils.java index aa60d1a6b4..db9466d723 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -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.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; @@ -30,21 +30,21 @@ public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getInstance().getWorldX(iter.x()); - final int wy = GeoEngine.getInstance().getWorldY(iter.y()); + final int wx = GeoEngine.getWorldX(iter.x()); + final int wy = GeoEngine.getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public final class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); - int wy = GeoEngine.getInstance().getWorldY(prevY); + int wx = GeoEngine.getWorldX(prevX); + int wy = GeoEngine.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.getInstance().getWorldX(curX); - wy = GeoEngine.getInstance().getWorldY(curY); + wx = GeoEngine.getWorldX(curX); + wy = GeoEngine.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().checkNearestNswe(x, y, z, nswe)) + if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0) { return Color.GREEN; } @@ -109,9 +109,8 @@ public final class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final GeoEngine ge = GeoEngine.getInstance(); - final int playerGx = ge.getGeoX(player.getX()); - final int playerGy = ge.getGeoY(player.getY()); + final int playerGx = GeoEngine.getGeoX(player.getX()); + final int playerGy = GeoEngine.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -135,32 +134,32 @@ public final class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = ge.getWorldX(gx); - final int y = ge.getWorldY(gy); - final int z = ge.getNearestZ(gx, gy, player.getZ()); + final int x = GeoEngine.getWorldX(gx); + final int y = GeoEngine.getWorldY(gy); + final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); + Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); 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, Cell.NSWE_EAST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); 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, Cell.NSWE_SOUTH); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); 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, Cell.NSWE_WEST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); 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); @@ -188,41 +187,41 @@ public final class GeoUtils { if (y > lastY) { - return Cell.NSWE_SOUTH_EAST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E; } else if (y < lastY) { - return Cell.NSWE_NORTH_EAST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E; } else { - return Cell.NSWE_EAST; + return GeoStructure.CELL_FLAG_E; } } else if (x < lastX) // west { if (y > lastY) { - return Cell.NSWE_SOUTH_WEST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W; } else if (y < lastY) { - return Cell.NSWE_NORTH_WEST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W; } else { - return Cell.NSWE_WEST; + return GeoStructure.CELL_FLAG_W; } } else // unchanged x { if (y > lastY) { - return Cell.NSWE_SOUTH; + return GeoStructure.CELL_FLAG_S; } else if (y < lastY) { - return Cell.NSWE_NORTH; + return GeoStructure.CELL_FLAG_N; } else { diff --git a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/util/Util.java b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/util/Util.java index 0f3288f8fb..56625c628e 100644 --- a/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/util/Util.java +++ b/L2J_Mobius_C4_ScionsOfDestiny/java/org/l2jmobius/gameserver/util/Util.java @@ -578,4 +578,15 @@ public class Util { return map.entrySet().stream().sorted(Map.Entry.comparingByValue()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); } + + /** + * @param numToTest : The number to test. + * @param min : The minimum limit. + * @param max : The maximum limit. + * @return the number or one of the limit (mininum / maximum). + */ + public static int limit(int numToTest, int min, int max) + { + return (numToTest > max) ? max : ((numToTest < min) ? min : numToTest); + } } diff --git a/L2J_Mobius_C6_Interlude/dist/game/config/main/GeoEngine.ini b/L2J_Mobius_C6_Interlude/dist/game/config/main/GeoEngine.ini index 4adfbea760..6e4b5db075 100644 --- a/L2J_Mobius_C6_Interlude/dist/game/config/main/GeoEngine.ini +++ b/L2J_Mobius_C6_Interlude/dist/game/config/main/GeoEngine.ini @@ -6,37 +6,52 @@ # at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ 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 + # ================================================================= -# Path finding +# Pathfinding # ================================================================= # 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 +# an alternative path (e.g. walk around obstacle), default: True +PathFinding = True -# Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 -PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 +# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 -# Weight for nodes without obstacles far from walls -LowWeight = 0.5 +# Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 +MoveWeight = 10 +MoveWeightDiag = 14 -# Weight for nodes near walls -MediumWeight = 2 +# 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 -# Weight for nodes with obstacles -HighWeight = 3 +# 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. -# Default: LowWeight * sqrt(2) -DiagonalWeight = 0.707 +# Maximum number of generated nodes per one path-finding process, default 3500 +MaxIterations = 3500 + +# ================================================================= +# 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 # ================================================================= # Other # ================================================================= -# Correct player Z after falling through the ground. -CorrectPlayerZ = False - # When a character falls, he takes damages. # Default: True FallDamage = True diff --git a/L2J_Mobius_C6_Interlude/dist/game/data/geodata/L2D_GeoEngine.patch b/L2J_Mobius_C6_Interlude/dist/game/data/geodata/L2D_GeoEngine.patch deleted file mode 100644 index e59fbf584a..0000000000 --- a/L2J_Mobius_C6_Interlude/dist/game/data/geodata/L2D_GeoEngine.patch +++ /dev/null @@ -1,5951 +0,0 @@ -Index: dist/game/GeoDataConverter.bat -=================================================================== ---- dist/game/GeoDataConverter.bat (nonexistent) -+++ dist/game/GeoDataConverter.bat (working copy) -@@ -0,0 +1,6 @@ -+@echo off -+title L2D geodata converter -+ -+java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter -+ -+pause -Index: dist/game/GeoDataConverter.sh -=================================================================== ---- dist/game/GeoDataConverter.sh (nonexistent) -+++ dist/game/GeoDataConverter.sh (working copy) -@@ -0,0 +1,4 @@ -+#! /bin/sh -+ -+java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 -+ -Index: dist/game/config/main/GeoEngine.ini -=================================================================== ---- dist/game/config/main/GeoEngine.ini (revision 8319) -+++ dist/game/config/main/GeoEngine.ini (working copy) -@@ -1,6 +1,10 @@ - # ================================================================= - # Geodata - # ================================================================= -+# Because of real-time performance we are using geodata files only in -+# diagonal L2D format now (using filename e.g. 22_16.l2d). -+# L2D geodata can be obtained by conversion of existing L2J or L2OFF geodata. -+# Launch "GeoDataConverter.bat/sh" and follow instructions to start the conversion. - - # 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/ -@@ -13,6 +17,16 @@ - CoordSynchronize = 2 - - # ================================================================= -+# Path checking -+# ================================================================= -+ -+# 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 finding - # ================================================================= - -@@ -23,19 +37,23 @@ - # Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - --# Weight for nodes without obstacles far from walls --LowWeight = 0.5 -+# Base path weight, when moving from one node to another on axis direction, default: 10 -+BaseWeight = 10 - --# Weight for nodes near walls --MediumWeight = 2 -+# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -+DiagonalWeight = 14 - --# Weight for nodes with obstacles --HighWeight = 3 -+# When movement flags of target node is blocked to any direction, multiply movement weight by this multiplier. -+# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 10 -+ObstacleMultiplier = 10 - --# Weight for diagonal movement. --# Default: LowWeight * sqrt(2) --DiagonalWeight = 0.707 -+# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 20 -+# For proper function must be higher than BaseWeight and/or DiagonalWeight. -+HeuristicWeight = 20 - -+# Maximum number of generated nodes per one path-finding process, default 3500 -+MaxIterations = 3500 -+ - # ================================================================= - # Other - # ================================================================= -Index: java/org/l2jmobius/Config.java -=================================================================== ---- java/org/l2jmobius/Config.java (revision 8388) -+++ java/org/l2jmobius/Config.java (working copy) -@@ -28,8 +28,6 @@ - import java.io.OutputStream; - import java.math.BigInteger; - import java.net.UnknownHostException; --import java.nio.file.Path; --import java.nio.file.Paths; - import java.util.ArrayList; - import java.util.HashMap; - import java.util.List; -@@ -964,14 +962,17 @@ - public static boolean LEAVE_BUFFS_ON_DIE; - public static boolean ALT_RAIDS_STATS_BONUS; - -- public static Path GEODATA_PATH; -+ public static String GEODATA_PATH; -+ public static int COORD_SYNCHRONIZE; -+ public static int PART_OF_CHARACTER_HEIGHT; -+ public static int MAX_OBSTACLE_HEIGHT; - public static boolean PATHFINDING; - public static String PATHFIND_BUFFERS; -- public static float LOW_WEIGHT; -- public static float MEDIUM_WEIGHT; -- public static float HIGH_WEIGHT; -- public static float DIAGONAL_WEIGHT; -- public static int COORD_SYNCHRONIZE; -+ public static int BASE_WEIGHT; -+ public static int DIAGONAL_WEIGHT; -+ public static int HEURISTIC_WEIGHT; -+ public static int OBSTACLE_MULTIPLIER; -+ public static int MAX_ITERATIONS; - public static boolean CORRECT_PLAYER_Z; - public static boolean FALL_DAMAGE; - public static boolean ALLOW_WATER; -@@ -2560,14 +2561,17 @@ - public static void loadgeodataConfig() - { - final PropertiesParser geoengineConfig = new PropertiesParser(GEOENGINE_CONFIG_FILE); -- GEODATA_PATH = Paths.get(geoengineConfig.getString("GeoDataPath", "./data/geodata")); -+ GEODATA_PATH = geoengineConfig.getString("GeoDataPath", "./data/geodata/"); -+ COORD_SYNCHRONIZE = geoengineConfig.getInt("CoordSynchronize", -1); -+ PART_OF_CHARACTER_HEIGHT = geoengineConfig.getInt("PartOfCharacterHeight", 75); -+ MAX_OBSTACLE_HEIGHT = geoengineConfig.getInt("MaxObstacleHeight", 32); - PATHFINDING = geoengineConfig.getBoolean("PathFinding", true); - 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); -- DIAGONAL_WEIGHT = geoengineConfig.getFloat("DiagonalWeight", 0.707f); -- COORD_SYNCHRONIZE = geoengineConfig.getInt("CoordSynchronize", -1); -+ BASE_WEIGHT = geoengineConfig.getInt("BaseWeight", 10); -+ DIAGONAL_WEIGHT = geoengineConfig.getInt("DiagonalWeight", 14); -+ OBSTACLE_MULTIPLIER = geoengineConfig.getInt("ObstacleMultiplier", 10); -+ HEURISTIC_WEIGHT = geoengineConfig.getInt("HeuristicWeight", 20); -+ MAX_ITERATIONS = geoengineConfig.getInt("MaxIterations", 3500); - CORRECT_PLAYER_Z = geoengineConfig.getBoolean("CorrectPlayerZ", false); - FALL_DAMAGE = geoengineConfig.getBoolean("FallDamage", false); - ALLOW_WATER = geoengineConfig.getBoolean("AllowWater", false); -Index: java/org/l2jmobius/gameserver/geoengine/GeoEngine.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (revision 8352) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (working copy) -@@ -16,98 +16,89 @@ - */ - package org.l2jmobius.gameserver.geoengine; - --import java.io.IOException; -+import java.io.File; - import java.io.RandomAccessFile; - import java.nio.ByteOrder; --import java.nio.channels.FileChannel.MapMode; --import java.nio.file.Files; --import java.nio.file.Path; --import java.util.concurrent.atomic.AtomicReferenceArray; --import java.util.logging.Level; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.List; - import java.util.logging.Logger; - - import org.l2jmobius.Config; - import org.l2jmobius.gameserver.data.xml.DoorData; - import org.l2jmobius.gameserver.data.xml.FenceData; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; --import org.l2jmobius.gameserver.geoengine.geodata.IRegion; --import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; --import org.l2jmobius.gameserver.geoengine.geodata.Region; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.World; - import org.l2jmobius.gameserver.model.WorldObject; --import org.l2jmobius.gameserver.util.GeoUtils; --import org.l2jmobius.gameserver.util.LinePointIterator; --import org.l2jmobius.gameserver.util.LinePointIterator3D; -+import org.l2jmobius.gameserver.model.actor.Creature; -+import org.l2jmobius.gameserver.util.MathUtil; - - /** -- * @author -Nemesiss-, HorridoJoho -+ * @author Hasha - */ - public class GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); -+ protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - -- private static final int WORLD_MIN_X = -655360; -- private static final int WORLD_MIN_Y = -589824; -- private static final int WORLD_MIN_Z = -16384; -+ private final ABlock[][] _blocks; -+ private final BlockNull _nullBlock; - -- /** 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; -- -- /** The regions array */ -- private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); -- -- private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; -- private static final int ELEVATED_SEE_OVER_DISTANCE = 2; -- private static final int MAX_SEE_OVER_HEIGHT = 48; -- -- protected GeoEngine() -+ /** -+ * GeoEngine constructor. Loads all geodata files of chosen geodata format. -+ */ -+ public GeoEngine() - { - LOGGER.info("GeoEngine: Initializing..."); -- for (int i = 0; i < _regions.length(); i++) -- { -- _regions.set(i, NullRegion.INSTANCE); -- } - -+ // initialize block container -+ _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; -+ -+ // load null block -+ _nullBlock = new BlockNull(); -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files according to geoengine config setup - int loaded = 0; -- try -+ long fileSize = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { -- for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { -- for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) -+ final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); -+ if (f.exists() && !f.isDirectory()) - { -- final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); -- if (Files.exists(geoFilePath)) -+ // region file is load-able, try to load it -+ if (loadGeoBlocks(rx, ry)) - { -- try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) -- { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -- loaded++; -- } -+ loaded++; -+ fileSize += f.length(); - } - } -+ else -+ { -+ // region file is not load-able, load null blocks -+ loadNullBlocks(rx, ry); -+ } - } - } -- catch (Exception e) -+ -+ LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -+ if (loaded > 0) - { -- LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); -- System.exit(1); -+ LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); - } - -- LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -- -- // Avoid wrong configs when no files are loaded. -+ // avoid wrong configs when no files are loaded - if (loaded == 0) - { - if (Config.PATHFINDING) -@@ -121,619 +112,820 @@ - LOGGER.info("GeoEngine: Forcing CoordSynchronize setting to -1."); - } - } -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); - } - - /** -- * @param geoX -- * @param geoY -- * @return the region -+ * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. -+ * @return boolean : True, when geodata file was loaded without problem. - */ -- private IRegion getRegion(int geoX, int geoY) -+ private final boolean loadGeoBlocks(int regionX, int regionY) - { -- final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); -- if ((region < 0) || (region >= _regions.length())) -+ final String filename = String.format(GeoFormat.L2D.getFilename(), regionX, regionY); -+ -+ // standard load -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) - { -- return null; -+ // initialize file buffer -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, GeoFormat.L2D); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ } -+ -+ // check data consistency -+ if (buffer.remaining() > 0) -+ { -+ LOGGER.warning("GeoEngine: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ } -+ -+ // loading was successful -+ return true; - } -- return _regions.get(region); -- } -- -- /** -- * @param filePath -- * @param regionX -- * @param regionY -- * @throws IOException -- */ -- 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")) -+ catch (Exception e) - { -- _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -+ // an error occured while loading, load null blocks -+ LOGGER.warning("GeoEngine: Error while loading " + filename + " region file."); -+ LOGGER.warning(e.getMessage()); -+ -+ // replace whole region file with null blocks -+ loadNullBlocks(regionX, regionY); -+ -+ // loading was not successful -+ return false; - } - } - - /** -- * @param regionX -- * @param regionY -+ * Loads null blocks. Used when no region file is detected or an error occurs during loading. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. - */ -- public void unloadRegion(int regionX, int regionY) -+ private final void loadNullBlocks(int regionX, int regionY) - { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); -- } -- -- /** -- * @param geoX -- * @param geoY -- * @return if geodata exist -- */ -- public boolean hasGeoPos(int geoX, int geoY) -- { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // load all null blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { -- return false; -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[blockX + ix][blockY + iy] = _nullBlock; -+ } - } -- return region.hasGeo(); - } - -+ // GEODATA - GENERAL -+ - /** -- * Checks the specified position for available geodata. -- * @param x the world x -- * @param y the world y -- * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise -+ * Converts world X to geodata X. -+ * @param worldX -+ * @return int : Geo X - */ -- public boolean hasGeo(int x, int y) -+ public static int getGeoX(int worldX) - { -- return hasGeoPos(getGeoX(x), getGeoY(y)); -+ return (MathUtil.limit(worldX, World.MAP_MIN_X, World.MAP_MAX_X) - World.MAP_MIN_X) >> 4; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe check -+ * Converts world Y to geodata Y. -+ * @param worldY -+ * @return int : Geo Y - */ -- public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) -+ public static int getGeoY(int worldY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return true; -- } -- return region.checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(worldY, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.MAP_MIN_Y) >> 4; - } - - /** -+ * Converts geodata X to world X. - * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe anti-corner cut -+ * @return int : World X - */ -- public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) -+ public static int getWorldX(int geoX) - { -- boolean can = true; -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- return can && checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(geoX, 0, GeoStructure.GEO_CELLS_X) << 4) + World.MAP_MIN_X + 8; - } - - /** -- * @param geoX -+ * Converts geodata Y to world Y. - * @param geoY -- * @param worldZ -- * @return the nearest Z value -+ * @return int : World Y - */ -- public int getNearestZ(int geoX, int geoY, int worldZ) -+ public static int getWorldY(int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return worldZ; -- } -- return region.getNearestZ(geoX, geoY, worldZ); -+ return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next lower Z value -+ * Returns block of geodata on given coordinates. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return {@link ABlock} : Block of geodata. - */ -- public int getNextLowerZ(int geoX, int geoY, int worldZ) -+ private final ABlock getBlock(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final int x = geoX / GeoStructure.BLOCK_CELLS_X; -+ final int y = geoY / GeoStructure.BLOCK_CELLS_Y; -+ -+ // if x or y is out of array return null -+ if ((x > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) - { -- return worldZ; -+ return _blocks[x][y]; - } -- return region.getNextLowerZ(geoX, geoY, worldZ); -+ return null; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next higher Z value -+ * Check if geo coordinates has geo. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return boolean : True, if given geo coordinates have geodata - */ -- public int getNextHigherZ(int geoX, int geoY, int worldZ) -+ public boolean hasGeoPos(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final ABlock block = getBlock(geoX, geoY); -+ if (block == null) // null block check - { -- return worldZ; -+ // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) -+ // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); -+ return false; - } -- return region.getNextHigherZ(geoX, geoY, worldZ); -+ return block.hasGeoPos(); - } - - /** -- * Gets the Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the height of cell, which is closest to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, closest to given coordinates. - */ -- public int getHeight(int x, int y, int z) -+ public short getHeightNearest(int geoX, int geoY, int worldZ) - { -- return getNearestZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getHeightNearest(geoX, geoY, worldZ) : (short) worldZ; - } - - /** -- * Gets the next lower Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the NSWE flag byte of cell, which is closes to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. - */ -- public int getLowerHeight(int x, int y, int z) -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) - { -- return getNextLowerZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getNsweNearest(geoX, geoY, worldZ) : (byte) 0xFF; - } - - /** -- * Gets the next higher Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Check if world coordinates has geo. -+ * @param worldX : World X -+ * @param worldY : World Y -+ * @return boolean : True, if given world coordinates have geodata - */ -- public int getHigherHeight(int x, int y, int z) -+ public boolean hasGeo(int worldX, int worldY) - { -- return getNextHigherZ(getGeoX(x), getGeoY(y), z); -+ return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); - } - - /** -- * @param worldX -- * @return the geo X -+ * Returns closest Z coordinate according to geodata. -+ * @param worldX : world x -+ * @param worldY : world y -+ * @param worldZ : world z -+ * @return short : nearest Z coordinates according to geodata - */ -- public int getGeoX(int worldX) -+ public short getHeight(int worldX, int worldY, int worldZ) - { -- return (worldX - WORLD_MIN_X) / 16; -+ return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - -- /** -- * @param worldY -- * @return the geo Y -- */ -- public int getGeoY(int worldY) -- { -- return (worldY - WORLD_MIN_Y) / 16; -- } -+ // PATHFINDING - - /** -- * @param worldZ -- * @return the geo Z -+ * Check line of sight from {@link WorldObject} to {@link WorldObject}. -+ * @param origin : The origin object. -+ * @param target : The target object. -+ * @return {@code boolean} : True if origin can see target - */ -- public int getGeoZ(int worldZ) -+ public boolean canSeeTarget(WorldObject origin, WorldObject target) - { -- return (worldZ - WORLD_MIN_Z) / 16; -- } -- -- /** -- * @param geoX -- * @return the world X -- */ -- public int getWorldX(int geoX) -- { -- return (geoX * 16) + WORLD_MIN_X + 8; -- } -- -- /** -- * @param geoY -- * @return the world Y -- */ -- public int getWorldY(int geoY) -- { -- return (geoY * 16) + WORLD_MIN_Y + 8; -- } -- -- /** -- * @param geoZ -- * @return the world Z -- */ -- public int getWorldZ(int geoZ) -- { -- return (geoZ * 16) + WORLD_MIN_Z + 8; -- } -- -- /** -- * Can see target. Doors as target always return true. Checks doors between. -- * @param cha the character -- * @param target the target -- * @return {@code true} if the character can see the target (LOS), {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, WorldObject target) -- { -- if (target.isDoor()) -+ if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) - { -- // Can always see doors. - return true; - } -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), target.getX(), target.getY(), target.getZ(), target.getInstanceId()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param cha the character -- * @param worldPosition the world position -- * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, Location worldPosition) -- { -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instanceId -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @param tInstanceId the target's instanceId -- * @return -- */ -- public boolean canSeeTarget(int x, int y, int z, int instanceId, int tx, int ty, int tz, int tInstanceId) -- { -- return (instanceId != tInstanceId) ? false : canSeeTarget(x, y, z, instanceId, tx, ty, tz); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instanceId -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -- */ -- public boolean canSeeTarget(int x, int y, int z, int instanceId, int tx, int ty, int tz) -- { -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz)) -+ -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = target.getX(); -+ final int ty = target.getY(); -+ final int tz = target.getZ(); -+ -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz)) - { - return false; - } -- return canSeeTarget(x, y, z, tx, ty, tz); -- } -- -- /** -- * @param prevX -- * @param prevY -- * @param prevGeoZ -- * @param curX -- * @param curY -- * @param nswe -- * @return the LOS Z value -- */ -- private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) -- { -- if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz)) - { -- throw new RuntimeException("Multiple directions!"); -+ return false; - } - -- if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- return getNearestZ(curX, curY, prevGeoZ); -+ return true; - } - -- return getNextHigherZ(curX, curY, prevGeoZ); -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) -+ { -+ return true; -+ } -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) -+ { -+ return goz == gtz; -+ } -+ -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) -+ { -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight() * 2; -+ } -+ -+ double theight = 0; -+ if (target.isCreature()) -+ { -+ theight = ((Creature) target).getTemplate().getCollisionHeight() * 2; -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceId()); - } - - /** -- * Can see target. Does not check doors between. -- * @param xValue the x coordinate -- * @param yValue the y coordinate -- * @param zValue the z coordinate -- * @param txValue the target's x coordinate -- * @param tyValue the target's y coordinate -- * @param tzValue the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -+ * Check line of sight from {@link WorldObject} to {@link Location}. -+ * @param origin : The origin object. -+ * @param position : The target position. -+ * @return {@code boolean} : True if object can see position - */ -- public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) -+ public boolean canSeeTarget(WorldObject origin, Location position) - { -- int x = xValue; -- int y = yValue; -- int tx = txValue; -- int ty = tyValue; -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = position.getX(); -+ final int ty = position.getY(); -+ final int tz = position.getZ(); - -- int geoX = getGeoX(x); -- int geoY = getGeoY(y); -- int tGeoX = getGeoX(tx); -- int tGeoY = getGeoY(ty); -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz)) -+ { -+ return false; -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz)) -+ { -+ return false; -+ } - -- int z = getNearestZ(geoX, geoY, zValue); -- int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- // Fastpath. -- if ((geoX == tGeoX) && (geoY == tGeoY)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- if (hasGeoPos(tGeoX, tGeoY)) -- { -- return z == tz; -- } - return true; - } - -- if (tz > z) -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) - { -- int tmp = tx; -- tx = x; -- x = tmp; -- -- tmp = ty; -- ty = y; -- y = tmp; -- -- tmp = tz; -- tz = z; -- z = tmp; -- -- tmp = tGeoX; -- tGeoX = geoX; -- geoX = tmp; -- -- tmp = tGeoY; -- tGeoY = geoY; -- geoY = tmp; -+ return goz == gtz; - } - -- final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); -- // First point is guaranteed to be available, skip it, we can always see our own position. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- final int prevZ = pointIter.z(); -- int prevGeoZ = prevZ; -- int ptIndex = 0; -- while (pointIter.next()) -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight(); -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceId()); -+ } -+ -+ /** -+ * Simple check for origin to target visibility. -+ * @param goxValue : origin X geodata coordinate -+ * @param goyValue : origin Y geodata coordinate -+ * @param gozValue : origin Z geodata coordinate -+ * @param oheight : origin height (if instance of {@link Character}) -+ * @param gtxValue : target X geodata coordinate -+ * @param gtyValue : target Y geodata coordinate -+ * @param gtzValue : target Z geodata coordinate -+ * @param theight : target height (if instance of {@link Character}) -+ * @param instanceId -+ * @return {@code boolean} : True, when target can be seen. -+ */ -+ private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, int instanceId) -+ { -+ int goz = gozValue; -+ int gtz = gtzValue; -+ int gox = goxValue; -+ int goy = goyValue; -+ int gtx = gtxValue; -+ int gty = gtyValue; -+ -+ // get line of sight Z coordinates -+ double losoz = goz + ((oheight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ double lostz = gtz + ((theight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ -+ // get X delta and signum -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirox = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; -+ final byte dirtx = sx > 0 ? GeoStructure.CELL_FLAG_W : GeoStructure.CELL_FLAG_E; -+ -+ // get Y delta and signum -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte diroy = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ final byte dirty = sy > 0 ? GeoStructure.CELL_FLAG_N : GeoStructure.CELL_FLAG_S; -+ -+ // get Z delta -+ final int dm = Math.max(dx, dy); -+ final double dz = (lostz - losoz) / dm; -+ -+ // get direction flag for diagonal movement -+ final byte diroxy = getDirXY(dirox, diroy); -+ final byte dirtxy = getDirXY(dirtx, dirty); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte diro; -+ byte dirt; -+ -+ // initialize node values -+ int nox = gox; -+ int noy = goy; -+ int ntx = gtx; -+ int nty = gty; -+ byte nsweo = getNsweNearest(gox, goy, goz); -+ byte nswet = getNsweNearest(gtx, gty, gtz); -+ -+ // loop -+ ABlock block; -+ int index; -+ for (int i = 0; i < ((dm + 1) / 2); i++) -+ { -+ // reset direction flag -+ diro = 0; -+ dirt = 0; - -- if ((curX == prevX) && (curY == prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- continue; -+ // calculate next point XY coordinates -+ d -= dy; -+ d += dx; -+ nox += sx; -+ ntx -= sx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroxy; -+ dirt |= dirtxy; - } -+ else if (e2 > -dy) -+ { -+ // calculate next point X coordinate -+ d -= dy; -+ nox += sx; -+ ntx -= sx; -+ diro |= dirox; -+ dirt |= dirtx; -+ } -+ else if (e2 < dx) -+ { -+ // calculate next point Y coordinate -+ d += dx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroy; -+ dirt |= dirty; -+ } - -- final int beeCurZ = pointIter.z(); -- int curGeoZ = prevGeoZ; -- -- // Check if the position has geodata. -- if (hasGeoPos(curX, curY)) - { -- final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); -- curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); -- int maxHeight; -- if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) -+ // get block of the next cell -+ block = getBlock(nox, noy); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nsweo & diro) == 0) - { -- maxHeight = z + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); - } - else - { -- maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); - } - -- boolean canSeeThrough = false; -- if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) -+ // layer does not exist, return -+ if (index == -1) - { -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else -- { -- canSeeThrough = true; -- } -+ return false; - } - -- if (!canSeeThrough) -+ // get layer and next line of sight Z coordinate -+ goz = block.getHeight(index); -+ losoz += dz; -+ -+ // perform line of sight check, return when fails -+ if ((goz - losoz) > Config.MAX_OBSTACLE_HEIGHT) - { - return false; - } -+ -+ // get layer nswe -+ nsweo = block.getNswe(index); - } -+ { -+ // get block of the next cell -+ block = getBlock(ntx, nty); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nswet & dirt) == 0) -+ { -+ index = block.getIndexAbove(ntx, nty, gtz - GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ else -+ { -+ index = block.getIndexBelow(ntx, nty, gtz + GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ -+ // layer does not exist, return -+ if (index == -1) -+ { -+ return false; -+ } -+ -+ // get layer and next line of sight Z coordinate -+ gtz = block.getHeight(index); -+ lostz -= dz; -+ -+ // perform line of sight check, return when fails -+ if ((gtz - lostz) > Config.MAX_OBSTACLE_HEIGHT) -+ { -+ return false; -+ } -+ -+ // get layer nswe -+ nswet = block.getNswe(index); -+ } - -- prevX = curX; -- prevY = curY; -- prevGeoZ = curGeoZ; -- ++ptIndex; -+ // update coords -+ gox = nox; -+ goy = noy; -+ gtx = ntx; -+ gty = nty; - } - -- return true; -+ // when iteration is completed, compare final Z coordinates -+ return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); - } - - /** -- * Move check. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param zValue the z coordinate -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tzValue the target's z coordinate -- * @param instanceId the instance id -- * @return the last Location (x,y,z) where player can walk - just before wall -+ * Check movement from coordinates to coordinates. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instanceId -+ * @return {code boolean} : True if target coordinates are reachable from origin coordinates - */ -- public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, int instanceId) -+ public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) - { -- final int geoX = getGeoX(x); -- final int geoY = getGeoY(y); -- final int z = getNearestZ(geoX, geoY, zValue); -- final int tGeoX = getGeoX(tx); -- final int tGeoY = getGeoY(ty); -- final int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } -- if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz)) -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = z; -+ // perform geodata check -+ final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instanceId); -+ return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); -+ } -+ -+ /** -+ * Check movement from origin to target. Returns last available point in the checked path. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instanceId -+ * @return {@link Location} : Last point where object can walk (just before wall) -+ */ -+ public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) -+ { -+ // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz)) -+ { -+ return new Location(ox, oy, oz); -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz)) -+ { -+ return new Location(ox, oy, oz); -+ } - -- while (pointIter.next()) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -- -- if (hasGeoPos(prevX, prevY)) -- { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -- { -- // Can't move, return previous location. -- return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); -- } -- } -- -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return new Location(tx, ty, tz); - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != tz)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- // Different floors, return start location. -- return new Location(x, y, z); -+ return new Location(tx, ty, tz); - } - -- return new Location(tx, ty, tz); -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) -+ { -+ return new Location(tx, ty, tz); -+ } -+ -+ // perform geodata check -+ return checkMove(gox, goy, goz, gtx, gty, gtz, instanceId); - } - - /** -- * Checks if its possible to move from one location to another. -- * @param fromX the X coordinate to start checking from -- * @param fromY the Y coordinate to start checking from -- * @param fromZvalue the Z coordinate to start checking from -- * @param toX the X coordinate to end checking at -- * @param toY the Y coordinate to end checking at -- * @param toZvalue the Z coordinate to end checking at -- * @param instanceId the instance -- * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise -+ * With this method you can check if a position is visible or can be reached by beeline movement.
-+ * Target X and Y reachable and Z is on same floor: -+ *
    -+ *
  • Location of the target with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y reachable but Z is on another floor: -+ *
    -+ *
  • Location of the origin with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y not reachable: -+ *
    -+ *
  • Last accessible location in destination to target.
  • -+ *
-+ * @param gox : origin X geodata coordinate -+ * @param goy : origin Y geodata coordinate -+ * @param goz : origin Z geodata coordinate -+ * @param gtx : target X geodata coordinate -+ * @param gty : target Y geodata coordinate -+ * @param gtz : target Z geodata coordinate -+ * @param instanceId -+ * @return {@link GeoLocation} : The last allowed point of movement. - */ -- public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, int instanceId) -+ protected final GeoLocation checkMove(int gox, int goy, int goz, int gtx, int gty, int gtz, int instanceId) - { -- final int geoX = getGeoX(fromX); -- final int geoY = getGeoY(fromY); -- final int fromZ = getNearestZ(geoX, geoY, fromZvalue); -- final int tGeoX = getGeoX(toX); -- final int tGeoY = getGeoY(toY); -- final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); -- -- if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ)) -+ if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } -- if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ)) -+ if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = fromZ; -+ // get X delta, signum and direction flag -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirX = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; - -- while (pointIter.next()) -+ // get Y delta, signum and direction flag -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte dirY = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ -+ // get direction flag for diagonal movement -+ final byte dirXY = getDirXY(dirX, dirY); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte direction; -+ -+ // load pointer coordinates -+ int gpx = gox; -+ int gpy = goy; -+ int gpz = goz; -+ -+ // load next pointer -+ int nx = gpx; -+ int ny = gpy; -+ -+ // loop -+ int count = 0; -+ while (count++ < Config.MAX_ITERATIONS) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -+ direction = 0; - -- if (hasGeoPos(prevX, prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -+ d -= dy; -+ d += dx; -+ nx += sx; -+ ny += sy; -+ direction |= dirXY; -+ } -+ else if (e2 > -dy) -+ { -+ d -= dy; -+ nx += sx; -+ direction |= dirX; -+ } -+ else if (e2 < dx) -+ { -+ d += dx; -+ ny += sy; -+ direction |= dirY; -+ } -+ -+ // obstacle found, return -+ if ((getNsweNearest(gpx, gpy, gpz) & direction) == 0) -+ { -+ return new GeoLocation(gpx, gpy, gpz); -+ } -+ -+ // update pointer coordinates -+ gpx = nx; -+ gpy = ny; -+ gpz = getHeightNearest(nx, ny, gpz); -+ -+ // target coordinates reached -+ if ((gpx == gtx) && (gpy == gty)) -+ { -+ if (gpz == gtz) - { -- return false; -+ // path found, Z coordinates are okay, return target point -+ return new GeoLocation(gtx, gty, gtz); - } -+ -+ // path found, Z coordinates are not okay, return last good point -+ return new GeoLocation(gpx, gpy, gpz); - } -+ } -+ -+ return new GeoLocation(gox, goy, goz); -+ } -+ -+ /** -+ * Returns diagonal NSWE flag format of combined two NSWE flags. -+ * @param dirX : X direction NSWE flag -+ * @param dirY : Y direction NSWE flag -+ * @return byte : NSWE flag of combined direction -+ */ -+ private static byte getDirXY(byte dirX, byte dirY) -+ { -+ // check axis directions -+ if (dirY == GeoStructure.CELL_FLAG_N) -+ { -+ if (dirX == GeoStructure.CELL_FLAG_W) -+ { -+ return GeoStructure.CELL_FLAG_NW; -+ } - -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return GeoStructure.CELL_FLAG_NE; - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) -+ if (dirX == GeoStructure.CELL_FLAG_W) - { -- // Different floors. -- return false; -+ return GeoStructure.CELL_FLAG_SW; - } - -- return true; -+ return GeoStructure.CELL_FLAG_SE; - } - -+ /** -+ * Returns the list of location objects as a result of complete path calculation. -+ * @param ox : origin x -+ * @param oy : origin y -+ * @param oz : origin z -+ * @param tx : target x -+ * @param ty : target y -+ * @param tz : target z -+ * @param instanceId -+ * @return {@code List} : complete path from nodes -+ */ -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) -+ { -+ return null; -+ } -+ -+ /** -+ * Returns the instance of the {@link GeoEngine}. -+ * @return {@link GeoEngine} : The instance. -+ */ - public static GeoEngine getInstance() - { - return SingletonHolder.INSTANCE; -@@ -741,6 +933,6 @@ - - private static class SingletonHolder - { -- protected static final GeoEngine INSTANCE = new GeoEngine(); -+ protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); - } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (working copy) -@@ -20,87 +20,83 @@ - import java.util.LinkedList; - import java.util.List; - import java.util.ListIterator; --import java.util.logging.Level; --import java.util.logging.Logger; - - import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; --import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; --import org.l2jmobius.gameserver.model.World; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -+import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -+import org.l2jmobius.gameserver.model.Location; - - /** -- * @author -Nemesiss- -+ * @author Hasha - */ --public class GeoEnginePathfinding -+final class GeoEnginePathfinding extends GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); -+ // pre-allocated buffers -+ private final BufferHolder[] _buffers; - -- private BufferInfo[] _buffers; -- - protected GeoEnginePathfinding() - { -- try -+ super(); -+ -+ final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ _buffers = new BufferHolder[array.length]; -+ int count = 0; -+ for (int i = 0; i < array.length; i++) - { -- final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ final String buf = array[i]; -+ final String[] args = buf.split("x"); - -- _buffers = new BufferInfo[array.length]; -- -- String buf; -- String[] args; -- for (int i = 0; i < array.length; i++) -+ try - { -- buf = array[i]; -- args = buf.split("x"); -- if (args.length != 2) -- { -- throw new Exception("Invalid buffer definition: " + buf); -- } -- -- _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); -+ final int size = Integer.parseInt(args[1]); -+ count += size; -+ _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); - } -+ catch (Exception e) -+ { -+ LOGGER.warning("GeoEnginePathfinding: Can not load buffer setting: " + buf); -+ } - } -- catch (Exception e) -- { -- LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); -- throw new Error("CellPathFinding: load aborted"); -- } -+ -+ LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); - } - -- public boolean pathNodesExist(short regionoffset) -+ @Override -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) - { -- return false; -- } -- -- public List findPath(int x, int y, int z, int tx, int ty, int tz, int instanceId) -- { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -- if (!GeoEngine.getInstance().hasGeo(x, y)) -+ // get origin and check existing geo coords -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { - 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)) -+ -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coords -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { - 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)))); -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // Prepare buffer for pathfinding calculations -+ final NodeBuffer buffer = getBuffer(64 + (2 * Math.max(Math.abs(gox - gtx), Math.abs(goy - gty)))); - if (buffer == null) - { - return null; - } - -- List path = null; -+ // find path -+ List path = null; - try - { -- final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); -- -+ final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); - if (result == null) - { - return null; -@@ -124,33 +120,45 @@ - return path; - } - -- int currentX, currentY, currentZ; -- ListIterator middlePoint; -+ // get path list iterator -+ final ListIterator point = path.listIterator(); - -- middlePoint = path.listIterator(); -- currentX = x; -- currentY = y; -- currentZ = z; -+ // get node A (origin) -+ int nodeAx = gox; -+ int nodeAy = goy; -+ short nodeAz = goz; - -- while (middlePoint.hasNext()) -+ // get node B -+ GeoLocation nodeB = (GeoLocation) point.next(); -+ -+ // iterate thought the path to optimize it -+ int count = 0; -+ while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) - { -- final AbstractNodeLoc locMiddle = middlePoint.next(); -- if (!middlePoint.hasNext()) -- { -- break; -- } -+ // get node C -+ final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - -- final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); -- if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instanceId)) -+ // check movement from node A to node C -+ final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instanceId); -+ if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) - { -- middlePoint.remove(); -+ // can move from node A to node C -+ -+ // remove node B -+ point.remove(); - } - else - { -- currentX = locMiddle.getX(); -- currentY = locMiddle.getY(); -- currentZ = locMiddle.getZ(); -+ // can not move from node A to node C -+ -+ // set node A (node B is part of path, update A coordinates) -+ nodeAx = nodeB.getGeoX(); -+ nodeAy = nodeB.getGeoY(); -+ nodeAz = (short) nodeB.getZ(); - } -+ -+ // set node B -+ nodeB = (GeoLocation) point.next(); - } - - return path; -@@ -157,144 +165,102 @@ - } - - /** -- * Convert geodata position to pathnode position -- * @param geo_pos -- * @return pathnode position -+ * Create list of node locations as result of calculated buffer node tree. -+ * @param node : the entry point -+ * @return List : list of node location - */ -- public short getNodePos(int geo_pos) -+ private static List constructPath(Node node) - { -- return (short) (geo_pos >> 3); // OK? -- } -- -- /** -- * Convert node position to pathnode block position -- * @param node_pos -- * @return pathnode block position (0...255) -- */ -- public short getNodeBlock(int node_pos) -- { -- return (short) (node_pos % 256); -- } -- -- public byte getRegionX(int node_pos) -- { -- return (byte) ((node_pos >> 8) + World.TILE_X_MIN); -- } -- -- public byte getRegionY(int node_pos) -- { -- return (byte) ((node_pos >> 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 node_x rx -- * @return -- */ -- public int calculateWorldX(short node_x) -- { -- return World.MAP_MIN_X + (node_x * 128) + 48; -- } -- -- /** -- * Convert pathnode y to World y position -- * @param node_y -- * @return -- */ -- public int calculateWorldY(short node_y) -- { -- return World.MAP_MIN_Y + (node_y * 128) + 48; -- } -- -- private List constructPath(AbstractNode nodeValue) -- { -- final LinkedList path = new LinkedList<>(); -- int previousDirectionX = Integer.MIN_VALUE; -- int previousDirectionY = Integer.MIN_VALUE; -- int directionX, directionY; -+ // create empty list -+ final LinkedList list = new LinkedList<>(); - -- AbstractNode node = nodeValue; -- while (node.getParent() != null) -+ // set direction X/Y -+ int dx = 0; -+ int dy = 0; -+ -+ // get target parent -+ Node target = node; -+ Node parent = target.getParent(); -+ -+ // while parent exists -+ int count = 0; -+ while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { -- directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); -- directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); -+ // get parent <> target direction X/Y -+ final int nx = parent.getLoc().getGeoX() - target.getLoc().getGeoX(); -+ final int ny = parent.getLoc().getGeoY() - target.getLoc().getGeoY(); - -- // only add a new route point if moving direction changes -- if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) -+ // direction has changed? -+ if ((dx != nx) || (dy != ny)) - { -- previousDirectionX = directionX; -- previousDirectionY = directionY; -+ // add node to the beginning of the list -+ list.addFirst(target.getLoc()); - -- path.addFirst(node.getLoc()); -- node.setLoc(null); -+ // update direction X/Y -+ dx = nx; -+ dy = ny; - } - -- node = node.getParent(); -+ // move to next node, set target and get its parent -+ target = parent; -+ parent = target.getParent(); - } - -- return path; -+ // return list -+ return list; - } - -- private CellNodeBuffer alloc(int size) -+ /** -+ * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer. -+ * @param size : pre-calculated minimal required size -+ * @return NodeBuffer : buffer -+ */ -+ private final NodeBuffer getBuffer(int size) - { -- CellNodeBuffer current = null; -- for (BufferInfo i : _buffers) -+ NodeBuffer current = null; -+ for (BufferHolder holder : _buffers) - { -- if (i.mapSize >= size) -+ // Find proper size of buffer -+ if (holder._size < size) - { -- for (CellNodeBuffer buf : i.buffer) -+ continue; -+ } -+ -+ // Find unlocked NodeBuffer -+ for (NodeBuffer buffer : holder._buffer) -+ { -+ if (!buffer.isLocked()) - { -- if (buf.lock()) -- { -- current = buf; -- break; -- } -+ continue; - } -- if (current != null) -- { -- break; -- } - -- // not found, allocate temporary buffer -- current = new CellNodeBuffer(i.mapSize); -- current.lock(); -- if (i.buffer.size() < i.count) -- { -- i.buffer.add(current); -- break; -- } -+ return buffer; - } -+ -+ // NodeBuffer not found, allocate temporary buffer -+ current = new NodeBuffer(holder._size); -+ current.isLocked(); - } - - return current; - } - -- private static final class BufferInfo -+ /** -+ * NodeBuffer container with specified size and count of separate buffers. -+ */ -+ private static final class BufferHolder - { -- final int mapSize; -- final int count; -- ArrayList buffer; -+ final int _size; -+ List _buffer; - -- public BufferInfo(int size, int cnt) -+ public BufferHolder(int size, int count) - { -- mapSize = size; -- count = cnt; -- buffer = new ArrayList<>(count); -+ _size = size; -+ _buffer = new ArrayList<>(count); -+ for (int i = 0; i < count; i++) -+ { -+ _buffer.add(new NodeBuffer(size)); -+ } - } - } -- -- public static GeoEnginePathfinding getInstance() -- { -- return SingletonHolder.INSTANCE; -- } -- -- private static class SingletonHolder -- { -- protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); -- } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (working copy) -@@ -0,0 +1,197 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @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 height of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getHeightNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first above given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, above given coordinates. -+ */ -+ public abstract short getHeightAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first below given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is closest to given coordinates. -+ * @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 the NSWE flag byte of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getNsweNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first above given coordinates. -+ * @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 getNsweAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first below given coordinates. -+ * @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 getNsweBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is closes layer to given coordinates. -+ * @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. -+ * @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 above given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is first layer below given coordinates. -+ * @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 index to data of the cell, which is first layer below given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexBelowOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeight(int index); -+ -+ /** -+ * Returns the height of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightOriginal(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNswe(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNsweOriginal(int index); -+ -+ /** -+ * Sets the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @param nswe : New NSWE flag byte. -+ */ -+ public abstract void setNswe(int index, byte nswe); -+ -+ /** -+ * Saves the block in L2D format to {@link BufferedOutputStream}. Used only for L2D geodata conversion. -+ * @param stream : The stream. -+ * @throws IOException : Can't save the block to steam. -+ */ -+ public abstract void saveBlock(BufferedOutputStream stream) throws IOException; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (working copy) -@@ -0,0 +1,252 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockComplex(ByteBuffer bb, GeoFormat format) -+ { -+ // initialize buffer -+ _buffer = new byte[GeoStructure.BLOCK_CELLS * 3]; -+ -+ // load data -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // 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); -+ } -+ else -+ { -+ // get nswe -+ final byte nswe = bb.get(); -+ _buffer[i * 3] = nswe; -+ -+ // get height -+ final short height = bb.getShort(); -+ _buffer[(i * 3) + 1] = (byte) (height & 0x00FF); -+ _buffer[(i * 3) + 2] = (byte) (height >> 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height > worldZ ? height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height < worldZ ? height : Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public 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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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 int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_COMPLEX_L2D); -+ -+ // write block data -+ stream.write(_buffer, 0, GeoStructure.BLOCK_CELLS * 3); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (working copy) -@@ -0,0 +1,176 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockFlat extends ABlock -+{ -+ protected final short _height; -+ protected byte _nswe; -+ -+ /** -+ * Creates FlatBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockFlat(ByteBuffer bb, GeoFormat format) -+ { -+ _height = bb.getShort(); -+ _nswe = format != GeoFormat.L2D ? 0x0F : (byte) (0xFF); -+ if (format == GeoFormat.L2OFF) -+ { -+ bb.getShort(); -+ } -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return true; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height > worldZ ? _height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height < worldZ ? _height : Short.MAX_VALUE; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height > worldZ ? _nswe : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height < worldZ ? _nswe : 0; -+ } -+ -+ @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return index -+ return _height < worldZ ? 0 : -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _nswe = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_FLAT_L2D); -+ -+ // write height -+ stream.write((byte) (_height & 0x00FF)); -+ stream.write((byte) (_height >> 8)); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (working copy) -@@ -0,0 +1,465 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+import java.nio.ByteOrder; -+import java.util.Arrays; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockMultilayer extends ABlock -+{ -+ private static final int MAX_LAYERS = Byte.MAX_VALUE; -+ -+ private static ByteBuffer _temp; -+ -+ /** -+ * Initializes the temporarily buffer. -+ */ -+ public static void initialize() -+ { -+ // initialize temporarily buffer and sorting mechanism -+ _temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3); -+ _temp.order(ByteOrder.LITTLE_ENDIAN); -+ } -+ -+ /** -+ * Releases temporarily buffer. -+ */ -+ public static void release() -+ { -+ _temp = null; -+ } -+ -+ protected byte[] _buffer; -+ -+ /** -+ * Implicit constructor for children class. -+ */ -+ protected BlockMultilayer() -+ { -+ _buffer = null; -+ } -+ -+ /** -+ * Creates MultilayerBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockMultilayer(ByteBuffer bb, GeoFormat format) -+ { -+ // 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 = format != GeoFormat.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++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // get data -+ final short data = bb.getShort(); -+ -+ // add nswe and height -+ _temp.put((byte) (data & 0x000F)); -+ _temp.putShort((short) ((short) (data & 0xFFF0) >> 1)); -+ } -+ else -+ { -+ // add nswe -+ _temp.put(bb.get()); -+ -+ // add height -+ _temp.putShort(bb.getShort()); -+ } -+ } -+ } -+ -+ // 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 height -+ if (height > worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, return minimum value -+ return Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 height -+ if (height < worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, return maximum value -+ return Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 nswe -+ if (height > worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 nswe -+ if (height < worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @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 first layer data (first from bottom) -+ byte layers = _buffer[index++]; -+ -+ // loop though all cell layers, find closest layer -+ 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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ // get height -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(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]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ // get nswe -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ // set nswe -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_MULTILAYER_L2D); -+ -+ // for each cell -+ int index = 0; -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ // write layers count -+ final byte layers = _buffer[index++]; -+ stream.write(layers); -+ -+ // write cell data -+ stream.write(_buffer, index, layers * 3); -+ -+ // move index to next cell -+ index += layers * 3; -+ } -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (working copy) -@@ -0,0 +1,150 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockNull extends ABlock -+{ -+ private final byte _nswe; -+ -+ public BlockNull() -+ { -+ _nswe = (byte) 0xFF; -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return false; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweBelow(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) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) -+ { -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (nonexistent) -@@ -1,48 +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 . -- */ --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() -- { -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (nonexistent) -@@ -1,77 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (nonexistent) -@@ -1,56 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (working copy) -@@ -0,0 +1,39 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public enum GeoFormat -+{ -+ L2J("%d_%d.l2j"), -+ L2OFF("%d_%d_conv.dat"), -+ L2D("%d_%d.l2d"); -+ -+ private final String _filename; -+ -+ private GeoFormat(String filename) -+ { -+ _filename = filename; -+ } -+ -+ public String getFilename() -+ { -+ return _filename; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (working copy) -@@ -0,0 +1,67 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.geoengine.GeoEngine; -+import org.l2jmobius.gameserver.model.Location; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoLocation extends Location -+{ -+ private byte _nswe; -+ -+ public GeoLocation(int x, int y, int z) -+ { -+ super(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public void set(int x, int y, short z) -+ { -+ super.setXYZ(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public int getGeoX() -+ { -+ return _x; -+ } -+ -+ public int getGeoY() -+ { -+ return _y; -+ } -+ -+ @Override -+ public int getX() -+ { -+ return GeoEngine.getWorldX(_x); -+ } -+ -+ @Override -+ public int getY() -+ { -+ return GeoEngine.getWorldY(_y); -+ } -+ -+ public byte getNSWE() -+ { -+ return _nswe; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (working copy) -@@ -0,0 +1,70 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoStructure -+{ -+ // cells -+ public static final byte CELL_FLAG_E = 1 << 0; -+ public static final byte CELL_FLAG_W = 1 << 1; -+ public static final byte CELL_FLAG_S = 1 << 2; -+ public static final byte CELL_FLAG_N = 1 << 3; -+ public static final byte CELL_FLAG_SE = 1 << 4; -+ public static final byte CELL_FLAG_SW = 1 << 5; -+ public static final byte CELL_FLAG_NE = 1 << 6; -+ public static final byte CELL_FLAG_NW = (byte) (1 << 7); -+ -+ public static final int CELL_HEIGHT = 8; -+ public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6; -+ -+ // blocks -+ public static final byte TYPE_FLAT_L2J_L2OFF = 0; -+ public static final byte TYPE_FLAT_L2D = (byte) 0xD0; -+ public static final byte TYPE_COMPLEX_L2J = 1; -+ public static final byte TYPE_COMPLEX_L2OFF = 0x40; -+ public static final byte TYPE_COMPLEX_L2D = (byte) 0xD1; -+ 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) -+ public static final byte TYPE_MULTILAYER_L2D = (byte) 0xD2; -+ -+ 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; -+ -+ // regions -+ 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; -+ -+ // global geodata -+ private static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); -+ private 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; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (nonexistent) -@@ -1,42 +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 . -- */ --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); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (working copy) -@@ -0,0 +1,53 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public interface IGeoObject -+{ -+ /** -+ * Returns geodata X coordinate of the {@link IGeoObject}. -+ * @return int : Geodata X coordinate. -+ */ -+ int getGeoX(); -+ -+ /** -+ * Returns geodata Y coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Y coordinate. -+ */ -+ int getGeoY(); -+ -+ /** -+ * Returns geodata Z coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Z coordinate. -+ */ -+ int getGeoZ(); -+ -+ /** -+ * Returns height of the {@link IGeoObject}. -+ * @return int : Height. -+ */ -+ int getHeight(); -+ -+ /** -+ * Returns {@link IGeoObject} data. -+ * @return byte[][] : {@link IGeoObject} data. -+ */ -+ byte[][] getObjectGeoData(); -+} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (nonexistent) -@@ -1,47 +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 . -- */ --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(); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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); -- -- // 1 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (nonexistent) -@@ -1,55 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --/** -- * @author HorridoJoho -- */ --public final class NullRegion implements IRegion --{ -- public static final NullRegion INSTANCE = new NullRegion(); -- -- @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 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Region.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (nonexistent) -@@ -1,92 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (nonexistent) -@@ -1,87 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public abstract class AbstractNode --{ -- private T _loc; -- private AbstractNode _parent; -- -- public AbstractNode(T loc) -- { -- _loc = loc; -- } -- -- public void setParent(AbstractNode p) -- { -- _parent = p; -- } -- -- public AbstractNode getParent() -- { -- return _parent; -- } -- -- public T getLoc() -- { -- return _loc; -- } -- -- public void setLoc(T l) -- { -- _loc = l; -- } -- -- @Override -- public int hashCode() -- { -- final int prime = 31; -- int result = 1; -- result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); -- return result; -- } -- -- @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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (nonexistent) -@@ -1,33 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --/** -- * @author -Nemesiss- -- */ --public abstract class AbstractNodeLoc --{ -- public abstract int getX(); -- -- public abstract int getY(); -- -- public abstract int getZ(); -- -- public abstract int getNodeX(); -- -- public abstract int getNodeY(); --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (nonexistent) -@@ -1,67 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public class CellNode extends AbstractNode --{ -- 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (nonexistent) -@@ -1,348 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --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 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) -- { -- _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(); -- } -- -- public final List debugPath() -- { -- final List 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 void getNeighbors() -- { -- if (!_current.getLoc().canGoAll()) -- { -- 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); -- } -- -- // SouthEast -- if ((nodeE != null) && (nodeS != null)) -- { -- if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) -- { -- addNode(x + 1, y + 1, z, true); -- } -- } -- -- // SouthWest -- if ((nodeS != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) -- { -- addNode(x - 1, y + 1, z, true); -- } -- } -- -- // NorthEast -- if ((nodeN != null) && (nodeE != null)) -- { -- if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) -- { -- addNode(x + 1, y - 1, z, true); -- } -- } -- -- // NorthWest -- if ((nodeN != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) -- { -- addNode(x - 1, y - 1, z, true); -- } -- } -- } -- -- private 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(); -- // reinit node if needed -- if (result.getLoc() != null) -- { -- result.getLoc().set(x, y, z); -- } -- else -- { -- result.setLoc(new NodeLoc(x, y, z)); -- } -- } -- -- return result; -- } -- -- private 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 boolean isHighWeight(int x, int y, int z) -- { -- final CellNode result = getNode(x, y, z); -- if (result == null) -- { -- return true; -- } -- -- if (!result.getLoc().canGoAll()) -- { -- return true; -- } -- if (Math.abs(result.getLoc().getZ() - z) > 16) -- { -- return true; -- } -- -- return false; -- } -- -- private 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (working copy) -@@ -0,0 +1,87 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+ -+/** -+ * @author Hasha -+ */ -+public class Node -+{ -+ // node coords and nswe flag -+ private GeoLocation _loc; -+ -+ // node parent (for reverse path construction) -+ private Node _parent; -+ // node child (for moving over nodes during iteration) -+ private Node _child; -+ -+ // node G cost (movement cost = parent movement cost + current movement cost) -+ private double _cost = -1000; -+ -+ public void setLoc(int x, int y, int z) -+ { -+ _loc = new GeoLocation(x, y, z); -+ } -+ -+ public GeoLocation getLoc() -+ { -+ return _loc; -+ } -+ -+ public void setParent(Node parent) -+ { -+ _parent = parent; -+ } -+ -+ public Node getParent() -+ { -+ return _parent; -+ } -+ -+ public void setChild(Node child) -+ { -+ _child = child; -+ } -+ -+ public Node getChild() -+ { -+ return _child; -+ } -+ -+ public void setCost(double cost) -+ { -+ _cost = cost; -+ } -+ -+ public double getCost() -+ { -+ return _cost; -+ } -+ -+ public void free() -+ { -+ // reset node location -+ _loc = null; -+ -+ // reset node parent, child and cost -+ _parent = null; -+ _child = null; -+ _cost = -1000; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (working copy) -@@ -0,0 +1,306 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import java.util.concurrent.locks.ReentrantLock; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+ -+/** -+ * @author DS, Hasha; Credits to Diamond -+ */ -+public class NodeBuffer -+{ -+ private final ReentrantLock _lock = new ReentrantLock(); -+ private final int _size; -+ private final Node[][] _buffer; -+ -+ // center coordinates -+ private int _cx = 0; -+ private int _cy = 0; -+ -+ // target coordinates -+ private int _gtx = 0; -+ private int _gty = 0; -+ private short _gtz = 0; -+ -+ private Node _current = null; -+ -+ /** -+ * Constructor of NodeBuffer. -+ * @param size : one dimension size of buffer -+ */ -+ public NodeBuffer(int size) -+ { -+ // set size -+ _size = size; -+ -+ // initialize buffer -+ _buffer = new Node[size][size]; -+ for (int x = 0; x < size; x++) -+ { -+ for (int y = 0; y < size; y++) -+ { -+ _buffer[x][y] = 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 Node : first node of path -+ */ -+ public Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) -+ { -+ // set coordinates (middle of the line (gox,goy) - (gtx,gty), will be in the center of the buffer) -+ _cx = gox + ((gtx - gox - _size) / 2); -+ _cy = goy + ((gty - goy - _size) / 2); -+ -+ _gtx = gtx; -+ _gty = gty; -+ _gtz = gtz; -+ -+ _current = getNode(gox, goy, goz); -+ _current.setCost(getCostH(gox, goy, goz)); -+ -+ int count = 0; -+ do -+ { -+ // reached target? -+ if ((_current.getLoc().getGeoX() == _gtx) && (_current.getLoc().getGeoY() == _gty) && (Math.abs(_current.getLoc().getZ() - _gtz) < 8)) -+ { -+ return _current; -+ } -+ -+ // expand current node -+ expand(); -+ -+ // move pointer -+ _current = _current.getChild(); -+ } -+ while ((_current != null) && (count++ < Config.MAX_ITERATIONS)); -+ -+ return null; -+ } -+ -+ public boolean isLocked() -+ { -+ return _lock.tryLock(); -+ } -+ -+ public void free() -+ { -+ _current = null; -+ -+ for (Node[] nodes : _buffer) -+ { -+ for (Node node : nodes) -+ { -+ if (node.getLoc() != null) -+ { -+ node.free(); -+ } -+ } -+ } -+ -+ _lock.unlock(); -+ } -+ -+ /** -+ * Check _current Node and add its neighbors to the buffer. -+ */ -+ private final void expand() -+ { -+ // can't move anywhere, don't expand -+ final byte nswe = _current.getLoc().getNSWE(); -+ if (nswe == 0) -+ { -+ return; -+ } -+ -+ // get geo coords of the node to be expanded -+ final int x = _current.getLoc().getGeoX(); -+ final int y = _current.getLoc().getGeoY(); -+ final short z = (short) _current.getLoc().getZ(); -+ -+ // can move north, expand -+ if ((nswe & GeoStructure.CELL_FLAG_N) != 0) -+ { -+ addNode(x, y - 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move south, expand -+ if ((nswe & GeoStructure.CELL_FLAG_S) != 0) -+ { -+ addNode(x, y + 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_W) != 0) -+ { -+ addNode(x - 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_E) != 0) -+ { -+ addNode(x + 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move north-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NW) != 0) -+ { -+ addNode(x - 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move north-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NE) != 0) -+ { -+ addNode(x + 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SW) != 0) -+ { -+ addNode(x - 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SE) != 0) -+ { -+ addNode(x + 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ } -+ -+ /** -+ * Returns node, if it exists in buffer. -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param z : node Z coord -+ * @return Node : node, if exits in buffer -+ */ -+ private final Node getNode(int x, int y, short z) -+ { -+ // check node X out of coordinates -+ final int ix = x - _cx; -+ if ((ix < 0) || (ix >= _size)) -+ { -+ return null; -+ } -+ -+ // check node Y out of coordinates -+ final int iy = y - _cy; -+ if ((iy < 0) || (iy >= _size)) -+ { -+ return null; -+ } -+ -+ // get node -+ final Node result = _buffer[ix][iy]; -+ -+ // check and update -+ if (result.getLoc() == null) -+ { -+ result.setLoc(x, y, z); -+ } -+ -+ // return node -+ return result; -+ } -+ -+ /** -+ * Add node given by coordinates to the buffer. -+ * @param x : geo X coord -+ * @param y : geo Y coord -+ * @param z : geo Z coord -+ * @param weight : weight of movement to new node -+ */ -+ private final void addNode(int x, int y, short z, int weight) -+ { -+ // get node to be expanded -+ final Node node = getNode(x, y, z); -+ if (node == null) -+ { -+ return; -+ } -+ -+ // Z distance between nearby cells is higher than cell size -+ if (node.getLoc().getZ() > (z + (2 * GeoStructure.CELL_HEIGHT))) -+ { -+ return; -+ } -+ -+ // node was already expanded, return -+ if (node.getCost() >= 0) -+ { -+ return; -+ } -+ -+ node.setParent(_current); -+ if (node.getLoc().getNSWE() != (byte) 0xFF) -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + (weight * Config.OBSTACLE_MULTIPLIER)); -+ } -+ else -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + weight); -+ } -+ -+ Node current = _current; -+ int count = 0; -+ while ((current.getChild() != null) && (count < (Config.MAX_ITERATIONS * 4))) -+ { -+ count++; -+ if (current.getChild().getCost() > node.getCost()) -+ { -+ node.setChild(current.getChild()); -+ break; -+ } -+ current = current.getChild(); -+ } -+ -+ if (count >= (Config.MAX_ITERATIONS * 4)) -+ { -+ System.err.println("Pathfinding: too long loop detected, cost:" + node.getCost()); -+ } -+ -+ current.setChild(node); -+ } -+ -+ /** -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param i : node Z coord -+ * @return double : node cost -+ */ -+ private final double getCostH(int x, int y, int i) -+ { -+ final int dX = x - _gtx; -+ final int dY = y - _gty; -+ final int dZ = (i - _gtz) / GeoStructure.CELL_HEIGHT; -+ -+ // return (Math.abs(dX) + Math.abs(dY) + Math.abs(dZ)) * Config.HEURISTIC_WEIGHT; // Manhattan distance -+ return Math.sqrt((dX * dX) + (dY * dY) + (dZ * dZ)) * Config.HEURISTIC_WEIGHT; // Direct distance -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -- --/** -- * @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 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 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; -- // return super.hashCode(); -- } -- -- @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; -- if (_x != other._x) -- { -- return false; -- } -- if (_y != other._y) -- { -- return false; -- } -- if (_goNorth != other._goNorth) -- { -- return false; -- } -- if (_goEast != other._goEast) -- { -- return false; -- } -- if (_goSouth != other._goSouth) -- { -- return false; -- } -- if (_goWest != other._goWest) -- { -- return false; -- } -- if (_geoHeight != other._geoHeight) -- { -- return false; -- } -- return true; -- } --} -Index: java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeodata.java -=================================================================== ---- java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeodata.java (revision 8319) -+++ java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeodata.java (working copy) -@@ -47,8 +47,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -@@ -63,8 +63,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -Index: java/org/l2jmobius/gameserver/model/actor/Creature.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/Creature.java (revision 8399) -+++ java/org/l2jmobius/gameserver/model/actor/Creature.java (working copy) -@@ -44,8 +44,6 @@ - import org.l2jmobius.gameserver.data.xml.ZoneData; - import org.l2jmobius.gameserver.enums.TeleportWhereType; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; - import org.l2jmobius.gameserver.handler.ISkillHandler; - import org.l2jmobius.gameserver.handler.SkillHandler; - import org.l2jmobius.gameserver.handler.itemhandlers.Potions; -@@ -4344,7 +4342,7 @@ - public int _heading; - public boolean disregardingGeodata; - public int onGeodataPathIndex; -- public List geoPath; -+ public List geoPath; - public int geoPathAccurateTx; - public int geoPathAccurateTy; - public int geoPathGtx; -@@ -5624,7 +5622,7 @@ - if (((originalDistance - distance) > 30) && !_isAfraid && !isInBoat) - { - // Path calculation -- overrides previous movement check -- m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId()); -+ m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId()); - if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found - { - if (isPlayer() && !_isFlying && !isInWater) -Index: java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (revision 8352) -+++ java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (working copy) -@@ -15849,7 +15849,7 @@ - { - if (Config.CORRECT_PLAYER_Z) - { -- final int nearestZ = GeoEngine.getInstance().getHigherHeight(getX(), getY(), getZ()); -+ final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); - if (getZ() < nearestZ) - { - teleToLocation(new Location(getX(), getY(), nearestZ), false); -Index: java/org/l2jmobius/gameserver/util/GeoUtils.java -=================================================================== ---- java/org/l2jmobius/gameserver/util/GeoUtils.java (revision 8319) -+++ java/org/l2jmobius/gameserver/util/GeoUtils.java (working copy) -@@ -19,7 +19,7 @@ - import java.awt.Color; - - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; - import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; - -@@ -26,25 +26,25 @@ - /** - * @author HorridoJoho - */ --public final class GeoUtils -+public class GeoUtils - { - public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); - - final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); - - while (iter.next()) - { -- final int wx = GeoEngine.getInstance().getWorldX(iter.x()); -- final int wy = GeoEngine.getInstance().getWorldY(iter.y()); -+ final int wx = GeoEngine.getWorldX(iter.x()); -+ final int wy = GeoEngine.getWorldY(iter.y()); - - prim.addPoint(Color.RED, wx, wy, z); - } -@@ -53,21 +53,21 @@ - - public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); -- int wy = GeoEngine.getInstance().getWorldY(prevY); -+ int wx = GeoEngine.getWorldX(prevX); -+ int wy = GeoEngine.getWorldY(prevY); - int wz = iter.z(); - prim.addPoint(Color.RED, wx, wy, wz); - -@@ -78,8 +78,8 @@ - - if ((curX != prevX) || (curY != prevY)) - { -- wx = GeoEngine.getInstance().getWorldX(curX); -- wy = GeoEngine.getInstance().getWorldY(curY); -+ wx = GeoEngine.getWorldX(curX); -+ wy = GeoEngine.getWorldY(curY); - wz = iter.z(); - - prim.addPoint(Color.RED, wx, wy, wz); -@@ -93,7 +93,7 @@ - - private static Color getDirectionColor(int x, int y, int z, int nswe) - { -- if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) -+ if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) - { - return Color.GREEN; - } -@@ -109,9 +109,8 @@ - int iPacket = 0; - - ExServerPrimitive exsp = null; -- final GeoEngine ge = GeoEngine.getInstance(); -- final int playerGx = ge.getGeoX(player.getX()); -- final int playerGy = ge.getGeoY(player.getY()); -+ final int playerGx = GeoEngine.getGeoX(player.getX()); -+ final int playerGy = GeoEngine.getGeoY(player.getY()); - for (int dx = -geoRadius; dx <= geoRadius; ++dx) - { - for (int dy = -geoRadius; dy <= geoRadius; ++dy) -@@ -135,12 +134,12 @@ - final int gx = playerGx + dx; - final int gy = playerGy + dy; - -- final int x = ge.getWorldX(gx); -- final int y = ge.getWorldY(gy); -- final int z = ge.getNearestZ(gx, gy, player.getZ()); -+ final int x = GeoEngine.getWorldX(gx); -+ final int y = GeoEngine.getWorldY(gy); -+ final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); - - // north arrow -- Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); -+ Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); - 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); -@@ -147,7 +146,7 @@ - exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z); - - // east arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); - 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); -@@ -154,13 +153,13 @@ - exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z); - - // south arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); - 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, Cell.NSWE_WEST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); - 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); -@@ -188,15 +187,15 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_EAST; -+ return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_EAST; -+ return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; - } - else - { -- return Cell.NSWE_EAST; -+ return GeoStructure.CELL_FLAG_E; // Direction.EAST; - } - } - else if (x < lastX) // west -@@ -203,26 +202,27 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_WEST; -+ return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_WEST; -+ return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; - } - else - { -- return Cell.NSWE_WEST; -+ return GeoStructure.CELL_FLAG_W; // Direction.WEST; - } - } -- else // unchanged x -+ else -+ // unchanged x - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH; -+ return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH; -+ return GeoStructure.CELL_FLAG_N; // Direction.NORTH; - } - else - { -Index: java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java -=================================================================== ---- java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (nonexistent) -+++ java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (working copy) -@@ -0,0 +1,367 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.tools.geodataconverter; -+ -+import java.io.BufferedOutputStream; -+import java.io.File; -+import java.io.FileOutputStream; -+import java.io.RandomAccessFile; -+import java.nio.ByteOrder; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.Scanner; -+ -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoDataConverter -+{ -+ final static String GEODATA_PATH = "./data/geodata/"; -+ -+ private static GeoFormat _format; -+ private static ABlock[][] _blocks; -+ -+ public static void main(String[] args) -+ { -+ // get geodata format -+ String type = ""; -+ try (Scanner scn = new Scanner(System.in)) -+ { -+ while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) -+ { -+ System.out.println("GeoDataConverter: Select source geodata type:"); -+ System.out.println(" J: L2J (e.g. 23_22.l2j)"); -+ System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); -+ System.out.println(" E: Exit"); -+ System.out.print("Choice: "); -+ type = scn.next(); -+ } -+ } -+ if (type.equalsIgnoreCase("E")) -+ { -+ System.exit(0); -+ } -+ -+ _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; -+ -+ // start conversion -+ System.out.println("GeoDataConverter: Converting all " + _format + " files."); -+ -+ // initialize geodata container -+ _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files -+ int converted = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) -+ { -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) -+ { -+ final String input = String.format(_format.getFilename(), rx, ry); -+ final String filepath = GEODATA_PATH; -+ final File f = new File(filepath + input); -+ if (f.exists() && !f.isDirectory()) -+ { -+ // load geodata -+ if (!loadGeoBlocks(input)) -+ { -+ System.out.println("GeoDataConverter: Unable to load " + input + " region file."); -+ continue; -+ } -+ -+ // recalculate nswe -+ if (!recalculateNswe()) -+ { -+ System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); -+ continue; -+ } -+ -+ // save geodata -+ final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); -+ if (!saveGeoBlocks(output)) -+ { -+ System.out.println("GeoDataConverter: Unable to save " + output + " region file."); -+ continue; -+ } -+ -+ converted++; -+ System.out.println("GeoDataConverter: Created " + output + " region file."); -+ } -+ } -+ } -+ System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); -+ } -+ -+ /** -+ * Loads geo blocks from buffer of the region file. -+ * @param filename : The name of the to load. -+ * @return boolean : True when successful. -+ */ -+ private static boolean loadGeoBlocks(String filename) -+ { -+ // region file is load-able, try to load it -+ try (RandomAccessFile raf = new RandomAccessFile(GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) -+ { -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) -+ if (_format == GeoFormat.L2OFF) -+ { -+ for (int i = 0; i < 18; i++) -+ { -+ buffer.get(); -+ } -+ } -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ if (_format == GeoFormat.L2J) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2J: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2J: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ else -+ { -+ // get block type -+ final short type = buffer.getShort(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ default: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ if (buffer.remaining() > 0) -+ { -+ System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ return false; -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); -+ return false; -+ } -+ } -+ -+ /** -+ * Recalculate diagonal flags for the region file. -+ * @return boolean : True when successful. -+ */ -+ private static boolean recalculateNswe() -+ { -+ try -+ { -+ for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) -+ { -+ for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) -+ { -+ // get block -+ final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // skip flat blocks -+ if (block instanceof BlockFlat) -+ { -+ continue; -+ } -+ -+ // for complex and multilayer blocks go though all layers -+ short height = Short.MAX_VALUE; -+ int index; -+ while ((index = block.getIndexBelow(x, y, height)) != -1) -+ { -+ // get height and nswe -+ height = block.getHeight(index); -+ byte nswe = block.getNswe(index); -+ -+ // update nswe with diagonal flags -+ nswe = updateNsweBelow(x, y, height, nswe); -+ -+ // set nswe of the cell -+ block.setNswe(index, nswe); -+ } -+ } -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ /** -+ * Updates the NSWE flag with diagonal flags. -+ * @param x : Geodata X coordinate. -+ * @param y : Geodata Y coordinate. -+ * @param z : Geodata Z coordinate. -+ * @param nsweValue : NSWE flag to be updated. -+ * @return byte : Updated NSWE flag. -+ */ -+ private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) -+ { -+ byte nswe = nsweValue; -+ -+ // calculate virtual layer height -+ final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); -+ -+ // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) -+ final byte nsweN = getNsweBelow(x, y - 1, height); -+ final byte nsweS = getNsweBelow(x, y + 1, height); -+ final byte nsweW = getNsweBelow(x - 1, y, height); -+ final byte nsweE = getNsweBelow(x + 1, y, height); -+ -+ // north-west -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NW; -+ } -+ -+ // north-east -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NE; -+ } -+ -+ // south-west -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SW; -+ } -+ -+ // south-east -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SE; -+ } -+ -+ return nswe; -+ } -+ -+ private static byte getNsweBelow(int geoX, int geoY, short worldZ) -+ { -+ // out of geo coordinates -+ if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) -+ { -+ return 0; -+ } -+ -+ // out of geo coordinates -+ if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) -+ { -+ return 0; -+ } -+ -+ // get block -+ final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // get index, when valid, return nswe -+ final int index = block.getIndexBelow(geoX, geoY, worldZ); -+ return index == -1 ? 0 : block.getNswe(index); -+ } -+ -+ /** -+ * Save region file to file. -+ * @param filename : The name of file to save. -+ * @return boolean : True when successful. -+ */ -+ private static boolean saveGeoBlocks(String filename) -+ { -+ try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) -+ { -+ // loop over region blocks and save each block -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[ix][iy].saveBlock(bos); -+ } -+ } -+ -+ // flush data to file -+ bos.flush(); -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+} -\ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/dist/game/data/geodata/Readme.txt b/L2J_Mobius_C6_Interlude/dist/game/data/geodata/Readme.txt index 5c756621e5..37ac3f8134 100644 --- a/L2J_Mobius_C6_Interlude/dist/game/data/geodata/Readme.txt +++ b/L2J_Mobius_C6_Interlude/dist/game/data/geodata/Readme.txt @@ -23,8 +23,8 @@ a - Prerequisites b - Make it work ---------------------------------------------- -To make geodata working: -* unpack your geodata files into "/data/geodata" folder -* open "/config/main/GeoEngine.ini" with your favorite text editor and then edit following config: - - CoordSynchronize = 2 -* If you do not use any geodata files, the server will automatically change this setting to -1. +To make geodata work: +* unpack your geodata files into "/data/geodata" folder (or any other folder) +* open "/config/main/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. diff --git a/L2J_Mobius_C6_Interlude/dist/game/data/scripts/ai/bosses/Antharas.java b/L2J_Mobius_C6_Interlude/dist/game/data/scripts/ai/bosses/Antharas.java index 46d3bcafec..a175651d2f 100644 --- a/L2J_Mobius_C6_Interlude/dist/game/data/scripts/ai/bosses/Antharas.java +++ b/L2J_Mobius_C6_Interlude/dist/game/data/scripts/ai/bosses/Antharas.java @@ -620,7 +620,7 @@ public class Antharas extends Quest final int ry = Rnd.get(112400, 116000); final int rdt = ((_antharas.getX() - rx) * (_antharas.getX() - rx)) + ((_antharas.getY() - ry) * (_antharas.getY() - ry)); final Location randomLocation = new Location(rx, ry, -7704); - if (GeoEngine.getInstance().canSeeTarget(_antharas, randomLocation) && (rdt < dt)) + if (GeoEngine.getInstance().canSeeLocation(_antharas, randomLocation) && (rdt < dt)) { x = rx; y = ry; diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/Config.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/Config.java index 7af2ea32b3..b86fe1e89f 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/Config.java @@ -42,6 +42,7 @@ import org.l2jmobius.commons.enums.ServerMode; import org.l2jmobius.commons.util.ClassMasterSettings; import org.l2jmobius.commons.util.PropertiesParser; import org.l2jmobius.commons.util.StringUtil; +import org.l2jmobius.gameserver.enums.GeoType; import org.l2jmobius.gameserver.model.olympiad.OlympiadPeriod; import org.l2jmobius.gameserver.util.FloodProtectorConfig; import org.l2jmobius.gameserver.util.Util; @@ -965,12 +966,17 @@ public class Config public static boolean ALT_RAIDS_STATS_BONUS; public static Path GEODATA_PATH; + public static GeoType GEODATA_TYPE; public static boolean PATHFINDING; public static String PATHFIND_BUFFERS; - public static float LOW_WEIGHT; - public static float MEDIUM_WEIGHT; - public static float HIGH_WEIGHT; - public static float DIAGONAL_WEIGHT; + public static int MOVE_WEIGHT; + public static int MOVE_WEIGHT_DIAG; + public static int OBSTACLE_WEIGHT; + 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 boolean FALL_DAMAGE; public static boolean ALLOW_WATER; @@ -2559,12 +2565,17 @@ public class Config { 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", "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); - DIAGONAL_WEIGHT = geoengineConfig.getFloat("DiagonalWeight", 0.707f); + PATHFIND_BUFFERS = geoengineConfig.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + MOVE_WEIGHT = geoengineConfig.getInt("MoveWeight", 10); + MOVE_WEIGHT_DIAG = geoengineConfig.getInt("MoveWeightDiag", 14); + OBSTACLE_WEIGHT = geoengineConfig.getInt("ObstacleWeight", 30); + 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); FALL_DAMAGE = geoengineConfig.getBoolean("FallDamage", false); ALLOW_WATER = geoengineConfig.getBoolean("AllowWater", false); } diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/commons/util/Point2D.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/commons/util/Point2D.java new file mode 100644 index 0000000000..ebb6272e5e --- /dev/null +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/commons/util/Point2D.java @@ -0,0 +1,180 @@ +/* + * 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 . + */ +package org.l2jmobius.commons.util; + +import java.util.Objects; + +/** + * A datatype used to retain a 2D (x/y) point. It got the capability to be set and cleaned. + */ +public class Point2D +{ + protected volatile int _x; + protected volatile int _y; + + public Point2D(int x, int y) + { + _x = x; + _y = y; + } + + @Override + public Point2D clone() + { + return new Point2D(_x, _y); + } + + @Override + public String toString() + { + return _x + ", " + _y; + } + + @Override + public int hashCode() + { + return Objects.hash(_x, _y); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final Point2D other = (Point2D) obj; + return (_x == other._x) && (_y == other._y); + } + + /** + * @param x : The X coord to test. + * @param y : The Y coord to test. + * @return True if all coordinates equals this {@link Point2D} coordinates. + */ + public boolean equals(int x, int y) + { + return (_x == x) && (_y == y); + } + + public int getX() + { + return _x; + } + + public void setX(int x) + { + _x = x; + } + + public int getY() + { + return _y; + } + + public void setY(int y) + { + _y = y; + } + + public void set(int x, int y) + { + _x = x; + _y = y; + } + + /** + * Refresh the current {@link Point2D} using a reference {@link Point2D} and a distance. The new destination is calculated to go in opposite side of the {@link Point2D} reference.
+ *
+ * This method is perfect to calculate fleeing characters position. + * @param referenceLoc : The Point2D used as position. + * @param distance : The distance to be set between current and new position. + */ + public void setFleeing(Point2D referenceLoc, int distance) + { + final double xDiff = referenceLoc.getX() - _x; + final double yDiff = referenceLoc.getY() - _y; + + final double yxRation = Math.abs(xDiff / yDiff); + + final int y = (int) (distance / (yxRation + 1)); + final int x = (int) (y * yxRation); + + _x += (xDiff < 0 ? x : -x); + _y += (yDiff < 0 ? y : -y); + } + + public void clean() + { + _x = 0; + _y = 0; + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @return The distance between this {@Point2D} and some given coordinates. + */ + public double distance2D(int x, int y) + { + final double dx = (double) _x - x; + final double dy = (double) _y - y; + + return Math.sqrt((dx * dx) + (dy * dy)); + } + + /** + * @param point : The {@link Point2D} to test. + * @return The distance between this {@Point2D} and the {@link Point2D} set as parameter. + */ + public double distance2D(Point2D point) + { + return distance2D(point.getX(), point.getY()); + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of some given coordinates. + */ + public boolean isIn2DRadius(int x, int y, int radius) + { + return distance2D(x, y) < radius; + } + + /** + * @param point : The Point2D to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of the {@link Point2D} set as parameter. + */ + public boolean isIn2DRadius(Point2D point, int radius) + { + return distance2D(point) < radius; + } +} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/data/xml/DoorData.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/data/xml/DoorData.java index 71fb74f06b..40539cebae 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/data/xml/DoorData.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/data/xml/DoorData.java @@ -234,12 +234,12 @@ public class DoorData implements IXmlReader } } - public boolean checkIfDoorsBetween(Location start, Location end) + public boolean checkIfDoorsBetween(Location start, Location end, int instanceId) { - return checkIfDoorsBetween(start.getX(), start.getY(), start.getZ(), end.getX(), end.getY(), end.getZ()); + return checkIfDoorsBetween(start.getX(), start.getY(), start.getZ(), end.getX(), end.getY(), end.getZ(), instanceId); } - public boolean checkIfDoorsBetween(int x, int y, int z, int tx, int ty, int tz) + public boolean checkIfDoorsBetween(int x, int y, int z, int tx, int ty, int tz, int instanceId) { final WorldRegion region = World.getInstance().getRegion(x, y); final Collection doors = region != null ? region.getDoors() : null; @@ -248,9 +248,9 @@ public class DoorData implements IXmlReader return false; } - for (DoorInstance doorInst : doors) + for (DoorInstance door : doors) { - if (doorInst.getXMax() == 0) + if ((door.getXMax() == 0) || (door.getInstanceId() != instanceId)) { continue; } @@ -259,32 +259,32 @@ public class DoorData implements IXmlReader // heavy approximation disabling some shooting angles especially near 2-piece doors // but most calculations should stop short // phase 1, x - if (((x <= doorInst.getXMax()) && (tx >= doorInst.getXMin())) || ((tx <= doorInst.getXMax()) && (x >= doorInst.getXMin()))) + if (((x <= door.getXMax()) && (tx >= door.getXMin())) || ((tx <= door.getXMax()) && (x >= door.getXMin()))) { // phase 2, y - if (((y <= doorInst.getYMax()) && (ty >= doorInst.getYMin())) || ((ty <= doorInst.getYMax()) && (y >= doorInst.getYMin()))) + if (((y <= door.getYMax()) && (ty >= door.getYMin())) || ((ty <= door.getYMax()) && (y >= door.getYMin()))) { // phase 3, basically only z remains but now we calculate it with another formula (by rage) // in some cases the direct line check (only) in the beginning isn't sufficient, // when char z changes a lot along the path - if ((doorInst.getStatus().getCurrentHp() > 0) && !doorInst.isOpen()) + if ((door.getStatus().getCurrentHp() > 0) && !door.isOpen()) { - final int px1 = doorInst.getXMin(); - final int py1 = doorInst.getYMin(); - final int pz1 = doorInst.getZMin(); - final int px2 = doorInst.getXMax(); - final int py2 = doorInst.getYMax(); - final int pz2 = doorInst.getZMax(); + final int px1 = door.getXMin(); + final int py1 = door.getYMin(); + final int pz1 = door.getZMin(); + final int px2 = door.getXMax(); + final int py2 = door.getYMax(); + final int pz2 = door.getZMax(); final int l = tx - x; final int m = ty - y; final int n = tz - z; - final int dk = ((doorInst.getA() * l) + (doorInst.getB() * m) + (doorInst.getC() * n)); + final int dk = ((door.getA() * l) + (door.getB() * m) + (door.getC() * n)); if (dk == 0) { continue; // Parallel } - final float p = (float) ((doorInst.getA() * x) + (doorInst.getB() * y) + (doorInst.getC() * z) + doorInst.getD()) / dk; + final float p = (float) ((door.getA() * x) + (door.getB() * y) + (door.getC() * z) + door.getD()) / dk; final int fx = (int) (x - (l * p)); final int fy = (int) (y - (m * p)); final int fz = (int) (z - (n * p)); diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/data/xml/FenceData.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/data/xml/FenceData.java index 924fca51ef..804598e7b5 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/data/xml/FenceData.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/data/xml/FenceData.java @@ -139,7 +139,7 @@ public class FenceData return _fences.get(objectId); } - public boolean checkIfFenceBetween(int x, int y, int z, int tx, int ty, int tz) + public boolean checkIfFenceBetween(int x, int y, int z, int tx, int ty, int tz, int instanceId) { final WorldRegion region = World.getInstance().getRegion(x, y); final List fences = region != null ? region.getFences() : null; @@ -151,7 +151,7 @@ public class FenceData for (FenceInstance fence : fences) { // Check if fence is geodata enabled. - if (!fence.getState().isGeodataEnabled()) + if (!fence.getState().isGeodataEnabled() || (fence.getInstanceId() != instanceId)) { continue; } diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/enums/GeoType.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/enums/GeoType.java new file mode 100644 index 0000000000..f77aa5eac4 --- /dev/null +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/enums/GeoType.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.enums; + +public enum GeoType +{ + L2J("%d_%d.l2j"), + L2OFF("%d_%d_conv.dat"); + + private final String _filename; + + private GeoType(String filename) + { + _filename = filename; + } + + public String getFilename() + { + return _filename; + } +} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java new file mode 100644 index 0000000000..e62f50a8df --- /dev/null +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java @@ -0,0 +1,144 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index b561f7dbed..17f2ed17d8 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,70 +16,62 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.channels.FileChannel.MapMode; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.logging.Level; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; import java.util.logging.Logger; import org.l2jmobius.Config; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; -import org.l2jmobius.gameserver.geoengine.geodata.IRegion; -import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; -import org.l2jmobius.gameserver.geoengine.geodata.Region; +import org.l2jmobius.gameserver.enums.GeoType; +import org.l2jmobius.gameserver.enums.MoveDirectionType; +import org.l2jmobius.gameserver.geoengine.geodata.ABlock; +import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; +import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; +import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; +import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; -import org.l2jmobius.gameserver.util.GeoUtils; -import org.l2jmobius.gameserver.util.LinePointIterator; -import org.l2jmobius.gameserver.util.LinePointIterator3D; +import org.l2jmobius.gameserver.model.actor.Creature; +import org.l2jmobius.gameserver.util.Util; -/** - * @author -Nemesiss-, HorridoJoho - */ public class GeoEngine { - private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - - private static final int WORLD_MIN_X = -655360; - private static final int WORLD_MIN_Y = -589824; - private static final int WORLD_MIN_Z = -16384; - - /** 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; - - /** The regions array */ - private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; - private static final int ELEVATED_SEE_OVER_DISTANCE = 2; - private static final int MAX_SEE_OVER_HEIGHT = 48; - protected GeoEngine() + private final ABlock[][] _blocks; + private final BlockNull _nullBlock; + + // Pre-allocated buffers. + private final BufferHolder[] _buffers; + + public GeoEngine() { LOGGER.info("GeoEngine: Initializing..."); - for (int i = 0; i < _regions.length(); i++) - { - _regions.set(i, NullRegion.INSTANCE); - } + // Initialize block container. + _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; + + // Load null block. + _nullBlock = new BlockNull(); + + // Initialize multilayer temporarily buffer. + BlockMultilayer.initialize(); + + // Load geo files according to geoengine config setup. int loaded = 0; try { @@ -90,23 +82,52 @@ public class GeoEngine final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); if (Files.exists(geoFilePath)) { - try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + // Region file is load-able, try to load it. + if (loadGeoBlocks(regionX, regionY)) { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); loaded++; } } + else + { + // Region file is not load-able, load null blocks. + loadNullBlocks(regionX, regionY); + } } } } catch (Exception e) { - LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + LOGGER.warning("GeoEngine: Failed to load geodata! " + e); System.exit(1); } - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + // Release multilayer block temporarily buffer. + BlockMultilayer.release(); + + String[] array = Config.PATHFIND_BUFFERS.split(";"); + _buffers = new BufferHolder[array.length]; + + int count = 0; + for (int i = 0; i < array.length; i++) + { + String buf = array[i]; + String[] args = buf.split("x"); + + try + { + int size = Integer.parseInt(args[1]); + count += size; + _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); + } + catch (Exception e) + { + LOGGER.warning("Could not load buffer setting:" + buf + ". " + e); + } + } + LOGGER.info("Loaded " + count + " node buffers."); + // Avoid wrong configs when no files are loaded. if ((loaded == 0) && Config.PATHFINDING) { @@ -116,616 +137,913 @@ public class GeoEngine } /** - * @param geoX - * @param geoY - * @return the region + * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer and log this situation. + * @param size : pre-calculated minimal required size + * @return NodeBuffer : buffer */ - private IRegion getRegion(int geoX, int geoY) + private NodeBuffer getBuffer(int size) { - final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); - if ((region < 0) || (region >= _regions.length())) + NodeBuffer current = null; + for (BufferHolder holder : _buffers) { - return null; - } - return _regions.get(region); - } - - /** - * @param filePath - * @param regionX - * @param regionY - * @throws IOException - */ - 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()).load().order(ByteOrder.LITTLE_ENDIAN))); - } - } - - /** - * @param regionX - * @param regionY - */ - public void unloadRegion(int regionX, int regionY) - { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); - } - - /** - * @param geoX - * @param geoY - * @return if geodata exist - */ - public boolean hasGeoPos(int geoX, int geoY) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return false; - } - return region.hasGeo(); - } - - /** - * Checks the specified position for available geodata. - * @param x the world x - * @param y the world y - * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise - */ - public boolean hasGeo(int x, int y) - { - return hasGeoPos(getGeoX(x), getGeoY(y)); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe check - */ - public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return true; - } - return region.checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe anti-corner cut - */ - public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) - { - boolean can = true; - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - return can && checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the nearest Z value - */ - public int getNearestZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNearestZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next lower Z value - */ - public int getNextLowerZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextLowerZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next higher Z value - */ - public int getNextHigherZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextHigherZ(geoX, geoY, worldZ); - } - - /** - * Gets the Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHeight(int x, int y, int z) - { - return getNearestZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next lower Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getLowerHeight(int x, int y, int z) - { - return getNextLowerZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next higher Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHigherHeight(int x, int y, int z) - { - return getNextHigherZ(getGeoX(x), getGeoY(y), z); - } - - /** - * @param worldX - * @return the geo X - */ - public int getGeoX(int worldX) - { - return (worldX - WORLD_MIN_X) / 16; - } - - /** - * @param worldY - * @return the geo Y - */ - public int getGeoY(int worldY) - { - return (worldY - WORLD_MIN_Y) / 16; - } - - /** - * @param worldZ - * @return the geo Z - */ - public int getGeoZ(int worldZ) - { - return (worldZ - WORLD_MIN_Z) / 16; - } - - /** - * @param geoX - * @return the world X - */ - public int getWorldX(int geoX) - { - return (geoX * 16) + WORLD_MIN_X + 8; - } - - /** - * @param geoY - * @return the world Y - */ - public int getWorldY(int geoY) - { - return (geoY * 16) + WORLD_MIN_Y + 8; - } - - /** - * @param geoZ - * @return the world Z - */ - public int getWorldZ(int geoZ) - { - return (geoZ * 16) + WORLD_MIN_Z + 8; - } - - /** - * Can see target. Doors as target always return true. Checks doors between. - * @param cha the character - * @param target the target - * @return {@code true} if the character can see the target (LOS), {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, WorldObject target) - { - if (target.isDoor()) - { - // Can always see doors. - return true; - } - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), target.getX(), target.getY(), target.getZ(), target.getInstanceId()); - } - - /** - * Can see target. Checks doors between. - * @param cha the character - * @param worldPosition the world position - * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, Location worldPosition) - { - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instanceId - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @param tInstanceId the target's instanceId - * @return - */ - public boolean canSeeTarget(int x, int y, int z, int instanceId, int tx, int ty, int tz, int tInstanceId) - { - return (instanceId != tInstanceId) ? false : canSeeTarget(x, y, z, instanceId, tx, ty, tz); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instanceId - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int x, int y, int z, int instanceId, int tx, int ty, int tz) - { - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz)) - { - return false; - } - return canSeeTarget(x, y, z, tx, ty, tz); - } - - /** - * @param prevX - * @param prevY - * @param prevGeoZ - * @param curX - * @param curY - * @param nswe - * @return the LOS Z value - */ - private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) - { - if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) - { - throw new RuntimeException("Multiple directions!"); - } - - if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) - { - return getNearestZ(curX, curY, prevGeoZ); - } - - return getNextHigherZ(curX, curY, prevGeoZ); - } - - /** - * Can see target. Does not check doors between. - * @param xValue the x coordinate - * @param yValue the y coordinate - * @param zValue the z coordinate - * @param txValue the target's x coordinate - * @param tyValue the target's y coordinate - * @param tzValue the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) - { - int x = xValue; - int y = yValue; - int tx = txValue; - int ty = tyValue; - - int geoX = getGeoX(x); - int geoY = getGeoY(y); - int tGeoX = getGeoX(tx); - int tGeoY = getGeoY(ty); - - int z = getNearestZ(geoX, geoY, zValue); - int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - // Fastpath. - if ((geoX == tGeoX) && (geoY == tGeoY)) - { - if (hasGeoPos(tGeoX, tGeoY)) - { - return z == tz; - } - return true; - } - - if (tz > z) - { - int tmp = tx; - tx = x; - x = tmp; - - tmp = ty; - ty = y; - y = tmp; - - tmp = tz; - tz = z; - z = tmp; - - tmp = tGeoX; - tGeoX = geoX; - geoX = tmp; - - tmp = tGeoY; - tGeoY = geoY; - geoY = tmp; - } - - final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); - // First point is guaranteed to be available, skip it, we can always see our own position. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - final int prevZ = pointIter.z(); - int prevGeoZ = prevZ; - int ptIndex = 0; - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - - if ((curX == prevX) && (curY == prevY)) + // Find proper size of buffer. + if (holder._size < size) { continue; } - final int beeCurZ = pointIter.z(); - int curGeoZ = prevGeoZ; - - // Check if the position has geodata. - if (hasGeoPos(curX, curY)) + // Find unlocked NodeBuffer. + for (NodeBuffer buffer : holder._buffer) { - final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); - curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); - int maxHeight; - if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) + if (!buffer.isLocked()) { - maxHeight = z + MAX_SEE_OVER_HEIGHT; - } - else - { - maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; + continue; } - boolean canSeeThrough = false; - if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) + return buffer; + } + + LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); + + // NodeBuffer not found, allocate temporary buffer. + current = new NodeBuffer(holder._size); + current.isLocked(); + } + + return current; + } + + /** + * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + * @return boolean : True, when geodata file was loaded without problem. + */ + private boolean loadGeoBlocks(int regionX, int regionY) + { + final String filename = String.format(Config.GEODATA_TYPE.getFilename(), regionX, regionY); + final String filepath = Config.GEODATA_PATH + "\\" + filename; + + // Standard load. + try (RandomAccessFile raf = new RandomAccessFile(filepath, "r"); + FileChannel fc = raf.getChannel()) + { + // Initialize file buffer. + MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // Load 18B header for L2OFF geodata (1st and 2nd byte...region X and Y). + if (Config.GEODATA_TYPE == GeoType.L2OFF) + { + for (int i = 0; i < 18; i++) { - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) + buffer.get(); + } + } + + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Loop over region blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + if (Config.GEODATA_TYPE == GeoType.L2J) { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) - { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); + // Get block type. + final byte type = buffer.get(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + case GeoStructure.TYPE_MULTILAYER_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + default: + { + throw new IllegalArgumentException("Unknown block type: " + type); + } + } } else { - canSeeThrough = true; + // Get block type. + final short type = buffer.getShort(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + default: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + } } } + } + + // Check data consistency. + if (buffer.remaining() > 0) + { + LOGGER.warning("Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); + } + + // Loading was successful. + return true; + } + catch (Exception e) + { + // An error occured while loading, load null blocks. + LOGGER.warning("Error loading " + filename + " region file. " + e); + + // Replace whole region file with null blocks. + loadNullBlocks(regionX, regionY); + + // Loading was not successful. + return false; + } + } + + /** + * Loads null blocks. Used when no region file is detected or an error occurs during loading. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + */ + private void loadNullBlocks(int regionX, int regionY) + { + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Load all null blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + _blocks[blockX + ix][blockY + iy] = _nullBlock; + } + } + } + + /** + * Converts world X to geodata X. + * @param worldX + * @return int : Geo X + */ + public static int getGeoX(int worldX) + { + return (worldX - World.WORLD_X_MIN) >> 4; + } + + /** + * Converts world Y to geodata Y. + * @param worldY + * @return int : Geo Y + */ + public static int getGeoY(int worldY) + { + return (worldY - World.WORLD_Y_MIN) >> 4; + } + + /** + * Converts geodata X to world X. + * @param geoX + * @return int : World X + */ + public static int getWorldX(int geoX) + { + return (geoX << 4) + World.WORLD_X_MIN + 8; + } + + /** + * Converts geodata Y to world Y. + * @param geoY + * @return int : World Y + */ + public static int getWorldY(int geoY) + { + return (geoY << 4) + World.WORLD_Y_MIN + 8; + } + + /** + * Returns block of geodata on given coordinates. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return {@link ABlock} : Block of geodata. + */ + public ABlock getBlock(int geoX, int geoY) + { + final int x = geoX / GeoStructure.BLOCK_CELLS_X; + if ((x < 0) || (x >= GeoStructure.GEO_BLOCKS_X)) + { + return null; + } + final int y = geoY / GeoStructure.BLOCK_CELLS_Y; + if ((y < 0) || (y >= GeoStructure.GEO_BLOCKS_Y)) + { + return null; + } + return _blocks[x][y]; + } + + /** + * Check if geo coordinates has geo. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return boolean : True, if given geo coordinates have geodata + */ + public boolean hasGeoPos(int geoX, int geoY) + { + final ABlock block = getBlock(geoX, geoY); + return (block != null) && block.hasGeoPos(); + } + + /** + * Returns the height of cell, which is closest to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell geodata Z coordinate, closest to given coordinates. + */ + public short getHeightNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return (short) worldZ; + } + return block.getHeightNearest(geoX, geoY, worldZ); + } + + /** + * Returns the NSWE flag byte of cell, which is closes to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. + */ + public byte getNsweNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return GeoStructure.CELL_FLAG_ALL; + } + return block.getNsweNearest(geoX, geoY, worldZ); + } + + /** + * Check if world coordinates has geo. + * @param worldX : World X + * @param worldY : World Y + * @return boolean : True, if given world coordinates have geodata + */ + public boolean hasGeo(int worldX, int worldY) + { + return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param loc : The location used as reference. + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(Location loc) + { + return getHeightNearest(getGeoX(loc.getX()), getGeoY(loc.getY()), loc.getZ()); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param worldX : world x + * @param worldY : world y + * @param worldZ : world z + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(int worldX, int worldY, int worldZ) + { + return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); + } + + /** + * Check line of sight from {@link WorldObject} to {@link WorldObject}.
+ * @param object : The origin object. + * @param target : The target object. + * @return True, when object can see target. + */ + public boolean canSeeTarget(WorldObject object, WorldObject target) + { + // Can always see doors. + if (target.isDoor()) + { + return true; + } + + if (object.getInstanceId() != target.getInstanceId()) + { + return false; + } + + if (DoorData.getInstance().checkIfDoorsBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceId())) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceId())) + { + return false; + } + + // Get object's and target's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getTemplate().getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + double theight = 0; + if (target instanceof Creature) + { + theight += (((Creature) target).getTemplate().getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + return canSee(object.getX(), object.getY(), object.getZ(), oheight, target.getX(), target.getY(), target.getZ(), theight) && canSee(target.getX(), target.getY(), target.getZ(), theight, object.getX(), object.getY(), object.getZ(), oheight); + } + + /** + * Check line of sight from {@link WorldObject} to {@link Location}.
+ * Note: The check uses {@link Location}'s real Z coordinate (e.g. point above ground), not its geodata representation. + * @param object : The origin object. + * @param position : The target position. + * @return True, when object can see position. + */ + public boolean canSeeLocation(WorldObject object, Location position) + { + // Get object and location coordinates. + int ox = object.getX(); + int oy = object.getY(); + int oz = object.getZ(); + int tx = position.getX(); + int ty = position.getY(); + int tz = position.getZ(); + + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, object.getInstanceId())) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, object.getInstanceId())) + { + return false; + } + + // Get object's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getTemplate().getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + // Perform geodata check. + return canSee(ox, oy, oz, oheight, tx, ty, tz, 0) && canSee(tx, ty, tz, 0, ox, oy, oz, oheight); + } + + /** + * Simple check for origin to target visibility.
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param oheight : The height of origin, used as start point. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param theight : The height of target, used as end point. + * @return True, when origin can see target. + */ + public boolean canSee(int ox, int oy, int oz, double oheight, int tx, int ty, int tz, double theight) + { + // Get origin geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get target geodata coordinates. + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check being on same cell and layer (index). + // Note: Get index must use origin height increased by cell height, the method returns index to height exclusive self. + int index = block.getIndexBelow(gox, goy, oz + GeoStructure.CELL_HEIGHT); + if (index < 0) + { + return false; + } + + if ((gox == gtx) && (goy == gty)) + { + return index == block.getIndexBelow(gtx, gty, tz + GeoStructure.CELL_HEIGHT); + } + + // Get ground and nswe flag. + int groundZ = block.getHeight(index); + int nswe = block.getNswe(index); + + // Get delta coordinates, slope of line (XY, XZ) and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double dz = (tz + theight) - (oz + oheight); + final double m = (double) dy / dx; + final double mz = dz / Math.sqrt((dx * dx) + (dy * dy)); + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); + + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) + { + // Set next cell in X direction. + gridX += mdt.getStepX(); + gox += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = Util.limit(checkX, gridX, gridX + 15); - if (!canSeeThrough) - { - return false; - } + // Set next cell in Y direction. + gridY += mdt.getStepY(); + goy += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevGeoZ = curGeoZ; - ++ptIndex; + // Get block of the next cell. + block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get line of sight height (including Z slope). + double losz = oz + oheight + Config.MAX_OBSTACLE_HEIGHT; + losz += mz * Math.sqrt(((checkX - ox) * (checkX - ox)) + ((checkY - oy) * (checkY - oy))); + + // Check line of sight going though wall (vertical check). + + // Get index of particular layer, based on last iterated cell conditions. + boolean canMove = (nswe & dir) != 0; + if (canMove) + { + // No wall present, get next cell below current cell. + index = block.getIndexBelow(gox, goy, groundZ + GeoStructure.CELL_IGNORE_HEIGHT); + } + else + { + // Wall present, get next cell above current cell. + index = block.getIndexAbove(gox, goy, groundZ - (2 * GeoStructure.CELL_HEIGHT)); + } + + // Next cell's does not exist (no geodata with valid condition), return fail. + if (index < 0) + { + return false; + } + + // Get next cell's layer height. + int z = block.getHeight(index); + + // Perform sine of sight check (next cell is above line of sight line), return fail. + if (z > losz) + { + return false; + } + + // Next cell is accessible, update z and NSWE. + groundZ = z; + nswe = block.getNswe(index); } + // Iteration is completed, no obstacle is found. return true; } /** - * Move check. - * @param x the x coordinate - * @param y the y coordinate - * @param zValue the z coordinate - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tzValue the target's z coordinate - * @param instanceId the instance id - * @return the last Location (x,y,z) where player can walk - just before wall + * Check movement from coordinates to coordinates.
+ * Note: The Z coordinates are supposed to be already validated geodata coordinates. + * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instanceId + * @return True, when target coordinates are reachable from origin coordinates. */ - public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, int instanceId) + public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) { - final int geoX = getGeoX(x); - final int geoY = getGeoY(y); - final int z = getNearestZ(geoX, geoY, zValue); - final int tGeoX = getGeoX(tx); - final int tGeoY = getGeoY(ty); - final int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instanceId)) { - return new Location(x, y, getHeight(x, y, z)); - } - if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz)) - { - return new Location(x, y, getHeight(x, y, z)); + return false; } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = z; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instanceId)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return false; + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + int goz = getHeightNearest(gox, goy, oz); + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check movement within same cell. + if ((gox == gtx) && (goy == gty)) + { + return goz == getHeight(tx, ty, tz); + } + + // Get nswe flag. + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - // Can't move, return previous location. - return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = Util.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check point heading into obstacle, if so return current point. + if ((nswe & dir) == 0) + { + return false; + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return false; + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != tz)) - { - // Different floors, return start location. - return new Location(x, y, z); - } - - return new Location(tx, ty, tz); + // When origin Z is target Z, the move is successful. + return goz == getHeight(tx, ty, tz); } /** - * Checks if its possible to move from one location to another. - * @param fromX the X coordinate to start checking from - * @param fromY the Y coordinate to start checking from - * @param fromZvalue the Z coordinate to start checking from - * @param toX the X coordinate to end checking at - * @param toY the Y coordinate to end checking at - * @param toZvalue the Z coordinate to end checking at - * @param instanceId the instance - * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + * Check movement from origin to target coordinates. Returns last available point in the checked path.
+ * Target X and Y reachable and Z is on same floor: + *
    + *
  • Location of the target with corrected Z value from geodata.
  • + *
+ * Target X and Y reachable but Z is on another floor: + *
    + *
  • Location of the origin with corrected Z value from geodata.
  • + *
+ * Target X and Y not reachable: + *
    + *
  • Last accessible location in destination to target.
  • + *
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instanceId + * @return The {@link Location} representing last point of movement (e.g. just before wall). */ - public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, int instanceId) + public Location getValidLocation(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) { - final int geoX = getGeoX(fromX); - final int geoY = getGeoY(fromY); - final int fromZ = getNearestZ(geoX, geoY, fromZvalue); - final int tGeoX = getGeoX(toX); - final int tGeoY = getGeoY(toY); - final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); - - if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instanceId)) { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ)) - { - return false; + return new Location(ox, oy, oz); } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = fromZ; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instanceId)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return new Location(ox, oy, oz); + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + final int gtz = getHeightNearest(gtx, gty, tz); + int goz = getHeightNearest(gox, goy, oz); + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - return false; - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = Util.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check target cell is out of geodata grid (world coordinates). + if ((nx < 0) || (nx >= GeoStructure.GEO_CELLS_X) || (ny < 0) || (ny >= GeoStructure.GEO_CELLS_Y)) + { + return new Location(checkX, checkY, goz); + } + + // Check point heading into obstacle, if so return current (border) point. + if ((nswe & dir) == 0) + { + return new Location(checkX, checkY, goz); + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current (border) point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return new Location(checkX, checkY, goz); + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) - { - // Different floors. - return false; - } - - return true; + // Compare Z coordinates: + // If same, path is okay, return target point and fix its Z geodata coordinate. + // If not same, path is does not exist, return origin point. + return goz == gtz ? new Location(tx, ty, gtz) : new Location(ox, oy, oz); } + /** + * Returns the list of location objects as a result of complete path calculation. + * @param ox : origin x + * @param oy : origin y + * @param oz : origin z + * @param tx : target x + * @param ty : target y + * @param tz : target z + * @param instanceId + * @return {@code List} : complete path from nodes + */ + public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) + { + // Get origin and check existing geo coords. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + if (!hasGeoPos(gox, goy)) + { + return Collections.emptyList(); + } + + int goz = getHeightNearest(gox, goy, oz); + + // Get target and check existing geo coords. + int gtx = getGeoX(tx); + int gty = getGeoY(ty); + if (!hasGeoPos(gtx, gty)) + { + return Collections.emptyList(); + } + + int gtz = getHeightNearest(gtx, gty, tz); + + // Prepare buffer for pathfinding calculations. + NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + if (buffer == null) + { + return Collections.emptyList(); + } + + // Find path. + List path = null; + try + { + path = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + if (path.isEmpty()) + { + return Collections.emptyList(); + } + } + catch (Exception e) + { + return Collections.emptyList(); + } + finally + { + buffer.free(); + } + + // Check path. + if (path.size() < 3) + { + return path; + } + + // Get path list iterator. + ListIterator point = path.listIterator(); + + // Get node A (origin). + int nodeAx = ox; + int nodeAy = oy; + int nodeAz = goz; + + // Get node B. + Location nodeB = point.next(); + + // Iterate thought the path to optimize it. + while (point.hasNext()) + { + // Get node C. + Location nodeC = path.get(point.nextIndex()); + + // Check movement from node A to node C. + if (canMoveToTarget(nodeAx, nodeAy, nodeAz, nodeC.getX(), nodeC.getY(), nodeC.getZ(), instanceId)) + { + // Can move from node A to node C. + // Remove node B. + point.remove(); + } + else + { + // Can not move from node A to node C. + // Set node A (node B is part of path, update A coordinates). + nodeAx = nodeB.getX(); + nodeAy = nodeB.getY(); + nodeAz = nodeB.getZ(); + } + + // Set node B. + nodeB = point.next(); + } + + return path; + } + + /** + * NodeBuffer container with specified size and count of separate buffers. + */ + private static class BufferHolder + { + final int _size; + final List _buffer; + + public BufferHolder(int size, int count) + { + _size = size; + _buffer = new ArrayList<>(count); + + for (int i = 0; i < count; i++) + { + _buffer.add(new NodeBuffer(size)); + } + } + } + + /** + * Returns the instance of the {@link GeoEngine}. + * @return {@link GeoEngine} : The instance. + */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -735,4 +1053,4 @@ public class GeoEngine { protected static final GeoEngine INSTANCE = new GeoEngine(); } -} +} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java deleted file mode 100644 index 43ac4d89b8..0000000000 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ /dev/null @@ -1,300 +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 . - */ -package org.l2jmobius.gameserver.geoengine; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; -import org.l2jmobius.gameserver.model.World; - -/** - * @author -Nemesiss- - */ -public class GeoEnginePathfinding -{ - private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); - - private BufferInfo[] _buffers; - - protected GeoEnginePathfinding() - { - try - { - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - - _buffers = 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); - } - - _buffers[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"); - } - } - - public boolean pathNodesExist(short regionoffset) - { - return false; - } - - public List findPath(int x, int y, int z, int tx, int ty, int tz, int instanceId) - { - 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)))); - if (buffer == null) - { - return null; - } - - List path = null; - try - { - final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); - - if (result == null) - { - return null; - } - - path = constructPath(result); - } - catch (Exception e) - { - LOGGER.warning(e.getMessage()); - return null; - } - finally - { - buffer.free(); - } - - // check path - if (path.size() < 3) - { - return path; - } - - int currentX, currentY, currentZ; - ListIterator middlePoint; - - middlePoint = path.listIterator(); - currentX = x; - currentY = y; - currentZ = z; - - while (middlePoint.hasNext()) - { - final AbstractNodeLoc locMiddle = middlePoint.next(); - if (!middlePoint.hasNext()) - { - break; - } - - final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); - if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instanceId)) - { - middlePoint.remove(); - } - else - { - currentX = locMiddle.getX(); - currentY = locMiddle.getY(); - currentZ = locMiddle.getZ(); - } - } - - return path; - } - - /** - * Convert geodata position to pathnode position - * @param geo_pos - * @return pathnode position - */ - public short getNodePos(int geo_pos) - { - return (short) (geo_pos >> 3); // OK? - } - - /** - * Convert node position to pathnode block position - * @param node_pos - * @return pathnode block position (0...255) - */ - public short getNodeBlock(int node_pos) - { - return (short) (node_pos % 256); - } - - public byte getRegionX(int node_pos) - { - return (byte) ((node_pos >> 8) + World.TILE_X_MIN); - } - - public byte getRegionY(int node_pos) - { - return (byte) ((node_pos >> 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 node_x rx - * @return - */ - public int calculateWorldX(short node_x) - { - return World.MAP_MIN_X + (node_x * 128) + 48; - } - - /** - * Convert pathnode y to World y position - * @param node_y - * @return - */ - public int calculateWorldY(short node_y) - { - return World.MAP_MIN_Y + (node_y * 128) + 48; - } - - private List constructPath(AbstractNode nodeValue) - { - final LinkedList path = new LinkedList<>(); - int previousDirectionX = Integer.MIN_VALUE; - int previousDirectionY = Integer.MIN_VALUE; - int directionX, directionY; - - AbstractNode node = nodeValue; - while (node.getParent() != null) - { - directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); - directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); - - // only add a new route point if moving direction changes - if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) - { - previousDirectionX = directionX; - previousDirectionY = directionY; - - path.addFirst(node.getLoc()); - node.setLoc(null); - } - - node = node.getParent(); - } - - return path; - } - - private CellNodeBuffer alloc(int size) - { - CellNodeBuffer current = null; - for (BufferInfo i : _buffers) - { - if (i.mapSize >= size) - { - for (CellNodeBuffer buf : i.buffer) - { - if (buf.lock()) - { - current = buf; - break; - } - } - if (current != null) - { - break; - } - - // not found, allocate temporary buffer - current = new CellNodeBuffer(i.mapSize); - current.lock(); - if (i.buffer.size() < i.count) - { - i.buffer.add(current); - break; - } - } - } - - return current; - } - - private static final class BufferInfo - { - final int mapSize; - final int count; - ArrayList buffer; - - public BufferInfo(int size, int cnt) - { - mapSize = size; - count = cnt; - buffer = new ArrayList<>(count); - } - } - - public static GeoEnginePathfinding getInstance() - { - return SingletonHolder.INSTANCE; - } - - private static class SingletonHolder - { - protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); - } -} diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java new file mode 100644 index 0000000000..49ec35aa1c --- /dev/null +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java @@ -0,0 +1,85 @@ +/* + * 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 . + */ +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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @param index : Index of the cell. + * @return short : Cell geodata Z coordinate, below given coordinates. + */ + public abstract byte getNswe(int index); +} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java new file mode 100644 index 0000000000..f5997bf287 --- /dev/null +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java @@ -0,0 +1,130 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java new file mode 100644 index 0000000000..9af9d2a28a --- /dev/null +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java @@ -0,0 +1,95 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java new file mode 100644 index 0000000000..31fbf5d477 --- /dev/null +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java new file mode 100644 index 0000000000..f77d21d84b --- /dev/null +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java @@ -0,0 +1,68 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +public class BlockNull extends ABlock +{ + @Override + public boolean hasGeoPos() + { + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java deleted file mode 100644 index 61ea4fcf82..0000000000 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java +++ /dev/null @@ -1,48 +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 . - */ -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() - { - } -} diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java deleted file mode 100644 index 3c8de1a7f7..0000000000 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java +++ /dev/null @@ -1,77 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java deleted file mode 100644 index 1ccdefe3da..0000000000 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java +++ /dev/null @@ -1,56 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java new file mode 100644 index 0000000000..691b164e0c --- /dev/null +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ +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_ALL = 0x0F; + + // 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; +} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java deleted file mode 100644 index 25eafc5a04..0000000000 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java +++ /dev/null @@ -1,42 +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 . - */ -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); -} diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java deleted file mode 100644 index 0d2c765002..0000000000 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java +++ /dev/null @@ -1,47 +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 . - */ -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(); -} diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java deleted file mode 100644 index 52df457360..0000000000 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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); - - // 1 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; - } -} diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java deleted file mode 100644 index 72a5a635c7..0000000000 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java +++ /dev/null @@ -1,55 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author HorridoJoho - */ -public final class NullRegion implements IRegion -{ - public static final NullRegion INSTANCE = new NullRegion(); - - @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 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; - } -} diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java deleted file mode 100644 index fc924cb875..0000000000 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java +++ /dev/null @@ -1,92 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java deleted file mode 100644 index 5087513ed9..0000000000 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java +++ /dev/null @@ -1,87 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public abstract class AbstractNode -{ - private T _loc; - private AbstractNode _parent; - - public AbstractNode(T loc) - { - _loc = loc; - } - - public void setParent(AbstractNode p) - { - _parent = p; - } - - public AbstractNode getParent() - { - return _parent; - } - - public T getLoc() - { - return _loc; - } - - public void setLoc(T l) - { - _loc = l; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); - return result; - } - - @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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java deleted file mode 100644 index 7223b5b2be..0000000000 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java +++ /dev/null @@ -1,33 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -/** - * @author -Nemesiss- - */ -public abstract class AbstractNodeLoc -{ - public abstract int getX(); - - public abstract int getY(); - - public abstract int getZ(); - - public abstract int getNodeX(); - - public abstract int getNodeY(); -} diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java deleted file mode 100644 index 3425234e0e..0000000000 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public class CellNode extends AbstractNode -{ - 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java deleted file mode 100644 index 522610bb7c..0000000000 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java +++ /dev/null @@ -1,348 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -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 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) - { - _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(); - } - - public final List debugPath() - { - final List 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 void getNeighbors() - { - if (!_current.getLoc().canGoAll()) - { - 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); - } - - // SouthEast - if ((nodeE != null) && (nodeS != null)) - { - if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) - { - addNode(x + 1, y + 1, z, true); - } - } - - // SouthWest - if ((nodeS != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) - { - addNode(x - 1, y + 1, z, true); - } - } - - // NorthEast - if ((nodeN != null) && (nodeE != null)) - { - if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) - { - addNode(x + 1, y - 1, z, true); - } - } - - // NorthWest - if ((nodeN != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) - { - addNode(x - 1, y - 1, z, true); - } - } - } - - private 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(); - // reinit node if needed - if (result.getLoc() != null) - { - result.getLoc().set(x, y, z); - } - else - { - result.setLoc(new NodeLoc(x, y, z)); - } - } - - return result; - } - - private 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 boolean isHighWeight(int x, int y, int z) - { - final CellNode result = getNode(x, y, z); - if (result == null) - { - return true; - } - - if (!result.getLoc().canGoAll()) - { - return true; - } - if (Math.abs(result.getLoc().getZ() - z) > 16) - { - return true; - } - - return false; - } - - private 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java new file mode 100644 index 0000000000..fa5ca43c2f --- /dev/null +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -0,0 +1,111 @@ +/* + * 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 . + */ +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 geodata values. + private int _geoX; + private int _geoY; + private byte _nswe; + + // 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; + + _costG = 0; + _costH = 0; + _costF = 0; + + _parent = null; + } + + public void setGeo(int gx, int gy, int gz, byte nswe) + { + super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); + + _geoX = gx; + _geoY = gy; + _nswe = nswe; + } + + 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 int getCostF() + { + return _costF; + } + + public Node getParent() + { + return _parent; + } + + @Override + public int compareTo(Node o) + { + return _costF - o._costF; + } +} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java new file mode 100644 index 0000000000..b464212c96 --- /dev/null +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -0,0 +1,368 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.awt.Color; +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; +import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; + +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 _opened; + // Container holding Nodes already explored. + private final List _closed; + + // Target coordinates. + private int _gtx; + private int _gty; + private int _gtz; + + // Pathfinding statistics. + private long _timeStamp; + private long _lastElapsedTime; + + 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 findPath(int gox, int goy, int goz, int gtx, int gty, int gtz) + { + // Set start timestamp. + _timeStamp = System.currentTimeMillis(); + + // 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)); + _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 constructPath() + { + // Create result. + final LinkedList 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; + } + + /** + * Creates list of Nodes to show debug path. + * @param debug : The debug packet to add debug informations in. + */ + public void debugPath(ExServerPrimitive debug) + { + // Add all opened node as yellow points. + for (Node n : _opened) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); + } + + // Add all opened node as blue points. + for (Node n : _closed) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); + } + } + + 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; + + _lastElapsedTime = System.currentTimeMillis() - _timeStamp; + _lock.unlock(); + } + + public long getElapsedTime() + { + return _lastElapsedTime; + } + + /** + * Expand the current {@link Node} by exploring its neighbors (axially and diagonally). + */ + private void expand() + { + // Movement is blocked, skip. + final byte nswe = _current.getNSWE(); + if (nswe == 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; + + // Can move north, expand. + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + } + + // Can move south, expand. + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + } + + // Can move west, expand. + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move east, expand. + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move north-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move north-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + } + + /** + * 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 weight : The weight of movement to the new node. + * @return The nswe of the added node. Blank, if not added. + */ + private byte addNode(int gx, int gy, int gzValue, int weight) + { + // 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]; + + // Set node geodata coordinates. + node.setGeo(gx, gy, gz, nswe); + + // 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, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + _opened.add(node); + _bufferIndex++; + return nswe; + } + + /** + * Calculate cost H value, calculated using diagonal distance method.
+ * 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. + final int dx = Math.abs(gx - _gtx); + final int dy = Math.abs(gy - _gty); + final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + + // Get diagonal and axial differences to the target. + final int dd = Math.min(dx, dy); + final int da = Math.max(dx, dy) - dd; + + // Calculate the diagonal distance of the node to the target. + return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + } +} \ No newline at end of file diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java deleted file mode 100644 index e3bad04b25..0000000000 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; - -/** - * @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 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 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; - // return super.hashCode(); - } - - @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; - if (_x != other._x) - { - return false; - } - if (_y != other._y) - { - return false; - } - if (_goNorth != other._goNorth) - { - return false; - } - if (_goEast != other._goEast) - { - return false; - } - if (_goSouth != other._goSouth) - { - return false; - } - if (_goWest != other._goWest) - { - return false; - } - if (_geoHeight != other._geoHeight) - { - return false; - } - return true; - } -} diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeodata.java index 893d3ef03a..16863403df 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminGeodata.java @@ -47,8 +47,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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); @@ -63,8 +63,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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); @@ -118,8 +118,8 @@ public class AdminGeodata implements IAdminCommandHandler } else if (command.equals("admin_geomap")) { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y); } else diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminZone.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminZone.java index d392f69966..6ea61d0142 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminZone.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/handler/admincommandhandlers/AdminZone.java @@ -88,8 +88,8 @@ public class AdminZone implements IAdminCommandHandler { final int x = activeChar.getX(); final int y = activeChar.getY(); - final int rx = ((x - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int ry = ((y - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int rx = ((x - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int ry = ((y - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final NpcHtmlMessage html = new NpcHtmlMessage(0); html.setFile("data/html/admin/zone.htm"); diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/handler/itemhandlers/RollingDice.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/handler/itemhandlers/RollingDice.java index 144c31a3db..5982af7701 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/handler/itemhandlers/RollingDice.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/handler/itemhandlers/RollingDice.java @@ -82,7 +82,7 @@ public class RollingDice implements IItemHandler final int x = player.getX() + x1; final int y = player.getY() + y1; final int z = player.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceId()); + final Location destination = GeoEngine.getInstance().getValidLocation(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceId()); Broadcast.toSelfAndKnownPlayers(player, new Dice(player.getObjectId(), item.getItemId(), number, destination.getX(), destination.getY(), destination.getZ())); final SystemMessage sm = new SystemMessage(SystemMessageId.S1_HAS_ROLLED_S2); diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/handler/skillhandlers/Fishing.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/handler/skillhandlers/Fishing.java index 250a6009eb..1e6c48b89d 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/handler/skillhandlers/Fishing.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/handler/skillhandlers/Fishing.java @@ -134,11 +134,11 @@ public class Fishing implements ISkillHandler if ((water != null)) { final Location waterLocation = new Location(x, y, water.getWaterZ() - 50); - if ((aimingTo != null) && GeoEngine.getInstance().canSeeTarget(player, waterLocation)) + if ((aimingTo != null) && GeoEngine.getInstance().canSeeLocation(player, waterLocation)) { z = water.getWaterZ() + 10; } - else if ((aimingTo != null) && GeoEngine.getInstance().canSeeTarget(player, waterLocation)) + else if ((aimingTo != null) && GeoEngine.getInstance().canSeeLocation(player, waterLocation)) { z = aimingTo.getWaterZ() + 10; } diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/Location.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/Location.java index 47181d1a9e..71dba4e137 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/Location.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/Location.java @@ -16,87 +16,88 @@ */ package org.l2jmobius.gameserver.model; -import org.l2jmobius.gameserver.model.actor.Creature; +import java.util.Objects; -public class Location +import org.l2jmobius.commons.util.Point2D; + +/** + * A datatype used to retain a 3D (x/y/z/heading) point. It got the capability to be set and cleaned. + */ +public class Location extends Point2D { public static final Location DUMMY_LOC = new Location(0, 0, 0); - protected int _x; - protected int _y; - protected int _z; - private int _heading = 0; + protected volatile int _z; + protected volatile int _heading; public Location(int x, int y, int z) { - _x = x; - _y = y; + super(x, y); _z = z; + _heading = 0; } public Location(int x, int y, int z, int heading) { - _x = x; - _y = y; + super(x, y); _z = z; _heading = heading; } public Location(WorldObject obj) { - _x = obj.getX(); - _y = obj.getY(); - _z = obj.getZ(); + this(obj.getX(), obj.getY(), obj.getZ(), obj.getPosition().getHeading(), obj.getInstanceId()); } - public Location(Creature obj) + public Location(int x, int y, int z, int heading, int instanceId) { - _x = obj.getX(); - _y = obj.getY(); - _z = obj.getZ(); - _heading = obj.getHeading(); + super(x, y); + _z = z; + _heading = heading; } + public Location(StatSet set) + { + super(set.getInt("x", 0), set.getInt("y", 0)); + _z = set.getInt("z", 0); + _heading = set.getInt("heading", 0); + } + + /** + * Get the x coordinate. + * @return the x coordinate + */ + @Override public int getX() { return _x; } + /** + * Get the y coordinate. + * @return the y coordinate + */ + @Override public int getY() { return _y; } + /** + * Get the z coordinate. + * @return the z coordinate + */ public int getZ() { return _z; } - public int getHeading() - { - return _heading; - } - - public void setX(int x) - { - _x = x; - } - - public void setY(int y) - { - _y = y; - } - - public void setZ(int z) - { - _z = z; - } - - public void setHeading(int head) - { - _heading = head; - } - + /** + * Set the x, y, z coordinates. + * @param x the x coordinate + * @param y the y coordinate + * @param z the z coordinate + */ public void setXYZ(int x, int y, int z) { _x = x; @@ -104,8 +105,85 @@ public class Location _z = z; } + /** + * Set the x, y, z coordinates. + * @param loc The location. + */ + public void setXYZ(Location loc) + { + setXYZ(loc.getX(), loc.getY(), loc.getZ()); + } + + /** + * Get the heading. + * @return the heading + */ + public int getHeading() + { + return _heading; + } + + /** + * Set the heading. + * @param heading the heading + */ + public void setHeading(int heading) + { + _heading = heading; + } + + public void setLocation(Location loc) + { + _x = loc.getX(); + _y = loc.getY(); + _z = loc.getZ(); + _heading = loc.getHeading(); + } + + @Override + public void clean() + { + super.clean(); + _z = 0; + } + + @Override + public Location clone() + { + return new Location(_x, _y, _z); + } + + @Override + public int hashCode() + { + return (31 * super.hashCode()) + Objects.hash(_z); + } + + /** + * @param x : The X coord to test. + * @param y : The Y coord to test. + * @param z : The X coord to test. + * @return True if all coordinates equals this {@link Point2D} coordinates. + */ public boolean equals(int x, int y, int z) { return (_x == x) && (_y == y) && (_z == z); } + + @Override + public boolean equals(Object obj) + { + if (obj instanceof Location) + { + final Location loc = (Location) obj; + return (getX() == loc.getX()) && (getY() == loc.getY()) && (getZ() == loc.getZ()) && (getHeading() == loc.getHeading()); + } + return false; + } + + @Override + public String toString() + { + return "[" + getClass().getSimpleName() + "] X: " + _x + " Y: " + _y + " Z: " + _z + " Heading: " + _heading; + } } diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/ObjectPosition.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/ObjectPosition.java index 889bb04153..e8e841c1a7 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/ObjectPosition.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/ObjectPosition.java @@ -99,23 +99,23 @@ public class ObjectPosition public void setXYZInvisible(int x, int y, int z) { int correctX = x; - if (correctX > World.MAP_MAX_X) + if (correctX > World.WORLD_X_MAX) { - correctX = World.MAP_MAX_X - 5000; + correctX = World.WORLD_X_MAX - 5000; } - if (correctX < World.MAP_MIN_X) + if (correctX < World.WORLD_X_MIN) { - correctX = World.MAP_MIN_X + 5000; + correctX = World.WORLD_X_MIN + 5000; } int correctY = y; - if (correctY > World.MAP_MAX_Y) + if (correctY > World.WORLD_Y_MAX) { - correctY = World.MAP_MAX_Y - 5000; + correctY = World.WORLD_Y_MAX - 5000; } - if (correctY < World.MAP_MIN_Y) + if (correctY < World.WORLD_Y_MIN) { - correctY = World.MAP_MIN_Y + 5000; + correctY = World.WORLD_Y_MIN + 5000; } setWorldPosition(correctX, correctY, z); @@ -180,15 +180,6 @@ public class ObjectPosition return getWorldPosition().getX(); } - /** - * Sets the x. - * @param value the new x - */ - public void setX(int value) - { - getWorldPosition().setX(value); - } - /** * Return the y position of the WorldObject. * @return the y @@ -198,15 +189,6 @@ public class ObjectPosition return getWorldPosition().getY(); } - /** - * Sets the y. - * @param value the new y - */ - public void setY(int value) - { - getWorldPosition().setY(value); - } - /** * Return the z position of the WorldObject. * @return the z @@ -216,15 +198,6 @@ public class ObjectPosition return getWorldPosition().getZ(); } - /** - * Sets the z. - * @param value the new z - */ - public void setZ(int value) - { - getWorldPosition().setZ(value); - } - /** * Gets the world position. * @return the world position diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/World.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/World.java index 6a55e72a68..bd84cc1b8f 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/World.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/World.java @@ -28,9 +28,6 @@ import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.actor.instance.PetInstance; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -/** - * @version $Revision: 1.21.2.5.2.7 $ $Date: 2005/03/27 15:29:32 $ - */ public class World { private static final Logger LOGGER = Logger.getLogger(World.class.getName()); @@ -38,32 +35,31 @@ public class World public static volatile int MAX_CONNECTED_COUNT = 0; public static volatile int OFFLINE_TRADE_COUNT = 0; - public static final int SHIFT_BY = 12; + /** Bit shift, defines number of regions note, shifting by 15 will result in regions corresponding to map tiles shifting by 11 divides one tile to 16x16 regions. */ + public static final int SHIFT_BY = 11; - // Geodata min/max tiles - public static final int TILE_X_MIN = 16; - public static final int TILE_X_MAX = 26; - public static final int TILE_Y_MIN = 10; - public static final int TILE_Y_MAX = 25; - - // Map dimensions public static final int TILE_SIZE = 32768; - public static final int MAP_MIN_X = (TILE_X_MIN - 20) * TILE_SIZE; - public static final int MAP_MAX_X = (TILE_X_MAX - 19) * TILE_SIZE; - public static final int MAP_MIN_Y = (TILE_Y_MIN - 18) * TILE_SIZE; - public static final int MAP_MAX_Y = (TILE_Y_MAX - 17) * TILE_SIZE; - /** calculated offset used so top left region is 0,0. */ - public static final int OFFSET_X = Math.abs(MAP_MIN_X >> SHIFT_BY); + /** Map dimensions. */ + public static final int TILE_X_MIN = 16; + public static final int TILE_Y_MIN = 10; + public static final int TILE_X_MAX = 26; + public static final int TILE_Y_MAX = 25; + public static final int TILE_ZERO_COORD_X = 20; + public static final int TILE_ZERO_COORD_Y = 18; + public static final int WORLD_X_MIN = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; + public static final int WORLD_Y_MIN = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; - /** The Constant OFFSET_Y. */ - public static final int OFFSET_Y = Math.abs(MAP_MIN_Y >> SHIFT_BY); + public static final int WORLD_X_MAX = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; + public static final int WORLD_Y_MAX = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; - /** number of regions. */ - private static final int REGIONS_X = (MAP_MAX_X >> SHIFT_BY) + OFFSET_X; + /** Calculated offset used so top left region is 0,0 */ + public static final int OFFSET_X = Math.abs(WORLD_X_MIN >> SHIFT_BY); + public static final int OFFSET_Y = Math.abs(WORLD_Y_MIN >> SHIFT_BY); - /** The Constant REGIONS_Y. */ - private static final int REGIONS_Y = (MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y; + /** Number of regions. */ + private static final int REGIONS_X = (WORLD_X_MAX >> SHIFT_BY) + OFFSET_X; + private static final int REGIONS_Y = (WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y; /** HashMap(String Player name, PlayerInstance) containing all the players in game. */ private static Map _allPlayers = new ConcurrentHashMap<>(); diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/WorldObject.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/WorldObject.java index c47018812d..42fc3feceb 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/WorldObject.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/WorldObject.java @@ -242,23 +242,23 @@ public abstract class WorldObject _isSpawned = true; int spawnX = x; - if (spawnX > World.MAP_MAX_X) + if (spawnX > World.WORLD_X_MAX) { - spawnX = World.MAP_MAX_X - 5000; + spawnX = World.WORLD_X_MAX - 5000; } - if (spawnX < World.MAP_MIN_X) + if (spawnX < World.WORLD_X_MIN) { - spawnX = World.MAP_MIN_X + 5000; + spawnX = World.WORLD_X_MIN + 5000; } int spawnY = y; - if (spawnY > World.MAP_MAX_Y) + if (spawnY > World.WORLD_Y_MAX) { - spawnY = World.MAP_MAX_Y - 5000; + spawnY = World.WORLD_Y_MAX - 5000; } - if (spawnY < World.MAP_MIN_Y) + if (spawnY < World.WORLD_Y_MIN) { - spawnY = World.MAP_MIN_Y + 5000; + spawnY = World.WORLD_Y_MIN + 5000; } getPosition().setWorldPosition(spawnX, spawnY, z); diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/actor/Creature.java index ee63634b13..f6dcc8f7dc 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -44,8 +44,6 @@ import org.l2jmobius.gameserver.data.xml.MapRegionData; import org.l2jmobius.gameserver.data.xml.ZoneData; import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.handler.ISkillHandler; import org.l2jmobius.gameserver.handler.SkillHandler; import org.l2jmobius.gameserver.handler.itemhandlers.Potions; @@ -4352,7 +4350,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder public int _heading; public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -5559,8 +5557,8 @@ public abstract class Creature extends WorldObject implements ISkillsHolder final int originalX = x; final int originalY = y; final int originalZ = z; - final int gtx = (originalX - World.MAP_MIN_X) >> 4; - final int gty = (originalY - World.MAP_MIN_Y) >> 4; + final int gtx = (originalX - World.WORLD_X_MIN) >> 4; + final int gty = (originalY - World.WORLD_Y_MIN) >> 4; if (isOnGeodataPath()) { // try @@ -5578,7 +5576,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder } // Temporary fix for character outside world region errors (should not happen) - if ((curX < World.MAP_MIN_X) || (curX > World.MAP_MAX_X) || (curY < World.MAP_MIN_Y) || (curY > World.MAP_MAX_Y)) + if ((curX < World.WORLD_X_MIN) || (curX > World.WORLD_X_MAX) || (curY < World.WORLD_Y_MIN) || (curY > World.WORLD_Y_MAX)) { LOGGER.warning("Character " + getName() + " outside world area, in coordinates x:" + curX + " y:" + curY); getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); @@ -5603,7 +5601,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder && !(((curZ - z) > 300) && (distance < 300))) // Prohibit correcting destination if character wants to fall. { // location different if destination wasn't reached (or just z coord is different) - final Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(curX, curY, curZ, x, y, z, getInstanceId()); + final Location destiny = GeoEngine.getInstance().getValidLocation(curX, curY, curZ, x, y, z, getInstanceId()); x = destiny.getX(); y = destiny.getY(); z = destiny.getZ(); @@ -5617,7 +5615,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder if (((originalDistance - distance) > 30) && !_isAfraid && !isInBoat) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId()); + m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { if (isPlayer() && !_isFlying && !isInWater) diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/actor/Summon.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/actor/Summon.java index 667d93cab6..62ed1915a8 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/actor/Summon.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/actor/Summon.java @@ -98,7 +98,7 @@ public abstract class Summon extends Playable final int x = owner.getX(); final int y = owner.getY(); final int z = owner.getZ(); - final Location location = GeoEngine.getInstance().canMoveToTargetLoc(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, getInstanceId()); + final Location location = GeoEngine.getInstance().getValidLocation(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, getInstanceId()); setXYZInvisible(location.getX(), location.getY(), location.getZ()); } diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java index 81f25573f3..d96a66816b 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java @@ -1106,7 +1106,7 @@ public class ItemInstance extends WorldObject int z = zValue; if (Config.PATHFINDING && (dropper != null)) { - final Location dropDest = GeoEngine.getInstance().canMoveToTargetLoc(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, dropper.getInstanceId()); + final Location dropDest = GeoEngine.getInstance().getValidLocation(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, dropper.getInstanceId()); if ((dropDest != null) && (dropDest.getX() != 0) && (dropDest.getY() != 0)) { x = dropDest.getX(); diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/skills/effects/EffectFear.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/skills/effects/EffectFear.java index 67fc3232be..9ec4af1c17 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/skills/effects/EffectFear.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/model/skills/effects/EffectFear.java @@ -119,7 +119,7 @@ final class EffectFear extends Effect posX += signx * FEAR_RANGE; posY += signy * FEAR_RANGE; - final Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(getEffected().getX(), getEffected().getY(), getEffected().getZ(), posX, posY, posZ, getEffected().getInstanceId()); + final Location destiny = GeoEngine.getInstance().getValidLocation(getEffected().getX(), getEffected().getY(), getEffected().getZ(), posX, posY, posZ, getEffected().getInstanceId()); getEffected().setRunning(); getEffected().getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location(destiny.getX(), destiny.getY(), destiny.getZ(), 0)); return true; diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java index f33cd87164..783106861f 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/network/clientpackets/MoveBackwardToLocation.java @@ -111,7 +111,7 @@ public class MoveBackwardToLocation extends GameClientPacket } // Mobius: Check for possible door logout and move over exploit. Also checked at ValidatePosition. - if (DoorData.getInstance().checkIfDoorsBetween(player.getX(), player.getY(), player.getZ(), _targetX, _targetY, _targetZ)) + if (DoorData.getInstance().checkIfDoorsBetween(player.getX(), player.getY(), player.getZ(), _targetX, _targetY, _targetZ, player.getInstanceId())) { player.stopMove(player.getLastServerPosition()); player.sendPacket(ActionFailed.STATIC_PACKET); diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java index 1d93093e3d..7f27a041f0 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java @@ -104,7 +104,7 @@ public class ValidatePosition extends GameClientPacket { if (player.isFalling(_z)) { - final int nearestZ = GeoEngine.getInstance().getHigherHeight(_x, _y, _z); + final int nearestZ = GeoEngine.getInstance().getHeight(_x, _y, _z); if (player.getZ() < nearestZ) { player.setXYZ(_x, _y, nearestZ); @@ -124,7 +124,7 @@ public class ValidatePosition extends GameClientPacket player.setClientHeading(_heading); // No real need to validate heading. // Mobius: Check for possible door logout and move over exploit. Also checked at MoveBackwardToLocation. - if (!DoorData.getInstance().checkIfDoorsBetween(realX, realY, realZ, _x, _y, _z)) + if (!DoorData.getInstance().checkIfDoorsBetween(realX, realY, realZ, _x, _y, _z, player.getInstanceId())) { player.setLastServerPosition(realX, realY, realZ); } diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/util/GeoUtils.java index aa60d1a6b4..db9466d723 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -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.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; @@ -30,21 +30,21 @@ public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getInstance().getWorldX(iter.x()); - final int wy = GeoEngine.getInstance().getWorldY(iter.y()); + final int wx = GeoEngine.getWorldX(iter.x()); + final int wy = GeoEngine.getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public final class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); - int wy = GeoEngine.getInstance().getWorldY(prevY); + int wx = GeoEngine.getWorldX(prevX); + int wy = GeoEngine.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.getInstance().getWorldX(curX); - wy = GeoEngine.getInstance().getWorldY(curY); + wx = GeoEngine.getWorldX(curX); + wy = GeoEngine.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().checkNearestNswe(x, y, z, nswe)) + if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0) { return Color.GREEN; } @@ -109,9 +109,8 @@ public final class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final GeoEngine ge = GeoEngine.getInstance(); - final int playerGx = ge.getGeoX(player.getX()); - final int playerGy = ge.getGeoY(player.getY()); + final int playerGx = GeoEngine.getGeoX(player.getX()); + final int playerGy = GeoEngine.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -135,32 +134,32 @@ public final class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = ge.getWorldX(gx); - final int y = ge.getWorldY(gy); - final int z = ge.getNearestZ(gx, gy, player.getZ()); + final int x = GeoEngine.getWorldX(gx); + final int y = GeoEngine.getWorldY(gy); + final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); + Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); 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, Cell.NSWE_EAST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); 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, Cell.NSWE_SOUTH); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); 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, Cell.NSWE_WEST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); 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); @@ -188,41 +187,41 @@ public final class GeoUtils { if (y > lastY) { - return Cell.NSWE_SOUTH_EAST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E; } else if (y < lastY) { - return Cell.NSWE_NORTH_EAST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E; } else { - return Cell.NSWE_EAST; + return GeoStructure.CELL_FLAG_E; } } else if (x < lastX) // west { if (y > lastY) { - return Cell.NSWE_SOUTH_WEST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W; } else if (y < lastY) { - return Cell.NSWE_NORTH_WEST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W; } else { - return Cell.NSWE_WEST; + return GeoStructure.CELL_FLAG_W; } } else // unchanged x { if (y > lastY) { - return Cell.NSWE_SOUTH; + return GeoStructure.CELL_FLAG_S; } else if (y < lastY) { - return Cell.NSWE_NORTH; + return GeoStructure.CELL_FLAG_N; } else { diff --git a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/util/Util.java b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/util/Util.java index 0f3288f8fb..56625c628e 100644 --- a/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/util/Util.java +++ b/L2J_Mobius_C6_Interlude/java/org/l2jmobius/gameserver/util/Util.java @@ -578,4 +578,15 @@ public class Util { return map.entrySet().stream().sorted(Map.Entry.comparingByValue()).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (e1, e2) -> e1, LinkedHashMap::new)); } + + /** + * @param numToTest : The number to test. + * @param min : The minimum limit. + * @param max : The maximum limit. + * @return the number or one of the limit (mininum / maximum). + */ + public static int limit(int numToTest, int min, int max) + { + return (numToTest > max) ? max : ((numToTest < min) ? min : numToTest); + } } diff --git a/L2J_Mobius_CT_2.4_Epilogue/dist/game/config/GeoEngine.ini b/L2J_Mobius_CT_2.4_Epilogue/dist/game/config/GeoEngine.ini index 609e8a89a7..e740c678dd 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_CT_2.4_Epilogue/dist/game/config/GeoEngine.ini @@ -6,33 +6,44 @@ # at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ 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 + # ================================================================= -# Path finding +# Pathfinding # ================================================================= # 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 +# an alternative path (e.g. walk around obstacle), default: True +PathFinding = True -# Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 -PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 +# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 -# Weight for nodes without obstacles far from walls -LowWeight = 0.5 +# Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 +MoveWeight = 10 +MoveWeightDiag = 14 -# Weight for nodes near walls -MediumWeight = 2 +# 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 -# Weight for nodes with obstacles -HighWeight = 3 +# 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. -# Default: LowWeight * sqrt(2) -DiagonalWeight = 0.707 +# Maximum number of generated nodes per one path-finding process, default 3500 +MaxIterations = 3500 # ================================================================= -# Other +# Line of Sight # ================================================================= -# Correct player Z after falling through the ground. -CorrectPlayerZ = False +# 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 diff --git a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/geodata/L2D_GeoEngine.patch b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/geodata/L2D_GeoEngine.patch deleted file mode 100644 index b11c767ce4..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/geodata/L2D_GeoEngine.patch +++ /dev/null @@ -1,6075 +0,0 @@ -Index: dist/game/GeoDataConverter.bat -=================================================================== ---- dist/game/GeoDataConverter.bat (nonexistent) -+++ dist/game/GeoDataConverter.bat (working copy) -@@ -0,0 +1,6 @@ -+@echo off -+title L2D geodata converter -+ -+java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter -+ -+pause -Index: dist/game/GeoDataConverter.sh -=================================================================== ---- dist/game/GeoDataConverter.sh (nonexistent) -+++ dist/game/GeoDataConverter.sh (working copy) -@@ -0,0 +1,4 @@ -+#! /bin/sh -+ -+java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 -+ -Index: dist/game/config/GeoEngine.ini -=================================================================== ---- dist/game/config/GeoEngine.ini (revision 8319) -+++ dist/game/config/GeoEngine.ini (working copy) -@@ -1,6 +1,10 @@ - # ================================================================= - # Geodata - # ================================================================= -+# Because of real-time performance we are using geodata files only in -+# diagonal L2D format now (using filename e.g. 22_16.l2d). -+# L2D geodata can be obtained by conversion of existing L2J or L2OFF geodata. -+# Launch "GeoDataConverter.bat/sh" and follow instructions to start the conversion. - - # 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/ -@@ -13,6 +17,16 @@ - CoordSynchronize = 2 - - # ================================================================= -+# Path checking -+# ================================================================= -+ -+# 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 finding - # ================================================================= - -@@ -23,19 +37,23 @@ - # Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - --# Weight for nodes without obstacles far from walls --LowWeight = 0.5 -+# Base path weight, when moving from one node to another on axis direction, default: 10 -+BaseWeight = 10 - --# Weight for nodes near walls --MediumWeight = 2 -+# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -+DiagonalWeight = 14 - --# Weight for nodes with obstacles --HighWeight = 3 -+# When movement flags of target node is blocked to any direction, multiply movement weight by this multiplier. -+# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 10 -+ObstacleMultiplier = 10 - --# Weight for diagonal movement. --# Default: LowWeight * sqrt(2) --DiagonalWeight = 0.707 -+# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 20 -+# For proper function must be higher than BaseWeight and/or DiagonalWeight. -+HeuristicWeight = 20 - -+# Maximum number of generated nodes per one path-finding process, default 3500 -+MaxIterations = 3500 -+ - # ================================================================= - # Other - # ================================================================= -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (revision 8319) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (working copy) -@@ -55,9 +55,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -@@ -73,9 +72,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (revision 8319) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (working copy) -@@ -18,11 +18,11 @@ - - import java.util.List; - --import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -+import org.l2jmobius.gameserver.geoengine.GeoEngine; - import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -+import org.l2jmobius.gameserver.network.SystemMessageId; - import org.l2jmobius.gameserver.util.BuilderUtil; - - public class AdminPathNode implements IAdminCommandHandler -@@ -37,29 +37,30 @@ - { - if (command.equals("admin_path_find")) - { -- if (!Config.PATHFINDING) -- { -- BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); -- return true; -- } - if (activeChar.getTarget() != null) - { -- final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceId()); -+ final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceId()); - if (path == null) - { -- BuilderUtil.sendSysMessage(activeChar, "No Route!"); -- return true; -+ BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); - } -- for (AbstractNodeLoc a : path) -+ else - { -- BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); -+ for (Location point : path) -+ { -+ BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); -+ } - } - } - else - { -- BuilderUtil.sendSysMessage(activeChar, "No Target!"); -+ activeChar.sendPacket(SystemMessageId.INVALID_TARGET); - } - } -+ else -+ { -+ return false; -+ } - return true; - } - -Index: java/org/l2jmobius/Config.java -=================================================================== ---- java/org/l2jmobius/Config.java (revision 8388) -+++ java/org/l2jmobius/Config.java (working copy) -@@ -32,7 +32,6 @@ - import java.net.UnknownHostException; - import java.nio.charset.StandardCharsets; - import java.nio.file.Files; --import java.nio.file.Path; - import java.nio.file.Paths; - import java.util.ArrayList; - import java.util.Arrays; -@@ -1059,14 +1058,17 @@ - // -------------------------------------------------- - // GeoEngine - // -------------------------------------------------- -- public static Path GEODATA_PATH; -+ public static String GEODATA_PATH; -+ public static int COORD_SYNCHRONIZE; -+ public static int PART_OF_CHARACTER_HEIGHT; -+ public static int MAX_OBSTACLE_HEIGHT; - public static boolean PATHFINDING; - public static String PATHFIND_BUFFERS; -- public static float LOW_WEIGHT; -- public static float MEDIUM_WEIGHT; -- public static float HIGH_WEIGHT; -- public static float DIAGONAL_WEIGHT; -- public static int COORD_SYNCHRONIZE; -+ public static int BASE_WEIGHT; -+ public static int DIAGONAL_WEIGHT; -+ public static int HEURISTIC_WEIGHT; -+ public static int OBSTACLE_MULTIPLIER; -+ public static int MAX_ITERATIONS; - public static boolean CORRECT_PLAYER_Z; - - // -------------------------------------------------- -@@ -2595,14 +2597,17 @@ - - // Load GeoEngine config file (if exists) - final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); -- GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); -+ GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); -+ COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); -+ MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); - PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -- LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); -- MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); -- HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); -- DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); -- COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); -+ DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); -+ OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); -+ HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); -+ MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); - CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); - - // Load AllowedPlayerRaces config file (if exists) -Index: java/org/l2jmobius/gameserver/ai/SummonAI.java -=================================================================== ---- java/org/l2jmobius/gameserver/ai/SummonAI.java (revision 8319) -+++ java/org/l2jmobius/gameserver/ai/SummonAI.java (working copy) -@@ -26,7 +26,6 @@ - import org.l2jmobius.commons.concurrent.ThreadPool; - import org.l2jmobius.commons.util.Rnd; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; - import org.l2jmobius.gameserver.model.WorldObject; - import org.l2jmobius.gameserver.model.actor.Creature; - import org.l2jmobius.gameserver.model.actor.Summon; -@@ -51,7 +50,7 @@ - @Override - protected void onIntentionAttack(Creature target) - { -- if (Config.PATHFINDING && (GeoEnginePathfinding.getInstance().findPath(_actor.getX(), _actor.getY(), _actor.getZ(), target.getX(), target.getY(), target.getZ(), _actor.getInstanceId()) == null)) -+ if (Config.PATHFINDING && (GeoEngine.getInstance().findPath(_actor.getX(), _actor.getY(), _actor.getZ(), target.getX(), target.getY(), target.getZ(), _actor.getInstanceId()) == null)) - { - return; - } -Index: java/org/l2jmobius/gameserver/geoengine/GeoEngine.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (revision 8352) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (working copy) -@@ -16,99 +16,89 @@ - */ - package org.l2jmobius.gameserver.geoengine; - --import java.io.IOException; -+import java.io.File; - import java.io.RandomAccessFile; - import java.nio.ByteOrder; --import java.nio.channels.FileChannel.MapMode; --import java.nio.file.Files; --import java.nio.file.Path; --import java.util.concurrent.atomic.AtomicReferenceArray; --import java.util.logging.Level; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.List; - import java.util.logging.Logger; - - import org.l2jmobius.Config; - import org.l2jmobius.gameserver.data.xml.DoorData; - import org.l2jmobius.gameserver.data.xml.FenceData; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; --import org.l2jmobius.gameserver.geoengine.geodata.IRegion; --import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; --import org.l2jmobius.gameserver.geoengine.geodata.Region; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.World; - import org.l2jmobius.gameserver.model.WorldObject; --import org.l2jmobius.gameserver.model.interfaces.ILocational; --import org.l2jmobius.gameserver.util.GeoUtils; --import org.l2jmobius.gameserver.util.LinePointIterator; --import org.l2jmobius.gameserver.util.LinePointIterator3D; -+import org.l2jmobius.gameserver.model.actor.Creature; -+import org.l2jmobius.gameserver.util.MathUtil; - - /** -- * @author -Nemesiss-, HorridoJoho -+ * @author Hasha - */ - public class GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); -+ protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - -- private static final int WORLD_MIN_X = -655360; -- private static final int WORLD_MIN_Y = -589824; -- private static final int WORLD_MIN_Z = -16384; -+ private final ABlock[][] _blocks; -+ private final BlockNull _nullBlock; - -- /** 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; -- -- /** The regions array */ -- private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); -- -- private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; -- private static final int ELEVATED_SEE_OVER_DISTANCE = 2; -- private static final int MAX_SEE_OVER_HEIGHT = 48; -- -- protected GeoEngine() -+ /** -+ * GeoEngine constructor. Loads all geodata files of chosen geodata format. -+ */ -+ public GeoEngine() - { - LOGGER.info("GeoEngine: Initializing..."); -- for (int i = 0; i < _regions.length(); i++) -- { -- _regions.set(i, NullRegion.INSTANCE); -- } - -+ // initialize block container -+ _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; -+ -+ // load null block -+ _nullBlock = new BlockNull(); -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files according to geoengine config setup - int loaded = 0; -- try -+ long fileSize = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { -- for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { -- for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) -+ final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); -+ if (f.exists() && !f.isDirectory()) - { -- final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); -- if (Files.exists(geoFilePath)) -+ // region file is load-able, try to load it -+ if (loadGeoBlocks(rx, ry)) - { -- try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) -- { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -- loaded++; -- } -+ loaded++; -+ fileSize += f.length(); - } - } -+ else -+ { -+ // region file is not load-able, load null blocks -+ loadNullBlocks(rx, ry); -+ } - } - } -- catch (Exception e) -+ -+ LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -+ if (loaded > 0) - { -- LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); -- System.exit(1); -+ LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); - } - -- LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -- -- // Avoid wrong configs when no files are loaded. -+ // avoid wrong configs when no files are loaded - if (loaded == 0) - { - if (Config.PATHFINDING) -@@ -122,619 +112,820 @@ - LOGGER.info("GeoEngine: Forcing CoordSynchronize setting to -1."); - } - } -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); - } - - /** -- * @param geoX -- * @param geoY -- * @return the region -+ * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. -+ * @return boolean : True, when geodata file was loaded without problem. - */ -- private IRegion getRegion(int geoX, int geoY) -+ private final boolean loadGeoBlocks(int regionX, int regionY) - { -- final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); -- if ((region < 0) || (region >= _regions.length())) -+ final String filename = String.format(GeoFormat.L2D.getFilename(), regionX, regionY); -+ -+ // standard load -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) - { -- return null; -+ // initialize file buffer -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, GeoFormat.L2D); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ } -+ -+ // check data consistency -+ if (buffer.remaining() > 0) -+ { -+ LOGGER.warning("GeoEngine: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ } -+ -+ // loading was successful -+ return true; - } -- return _regions.get(region); -- } -- -- /** -- * @param filePath -- * @param regionX -- * @param regionY -- * @throws IOException -- */ -- 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")) -+ catch (Exception e) - { -- _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -+ // an error occured while loading, load null blocks -+ LOGGER.warning("GeoEngine: Error while loading " + filename + " region file."); -+ LOGGER.warning(e.getMessage()); -+ -+ // replace whole region file with null blocks -+ loadNullBlocks(regionX, regionY); -+ -+ // loading was not successful -+ return false; - } - } - - /** -- * @param regionX -- * @param regionY -+ * Loads null blocks. Used when no region file is detected or an error occurs during loading. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. - */ -- public void unloadRegion(int regionX, int regionY) -+ private final void loadNullBlocks(int regionX, int regionY) - { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); -- } -- -- /** -- * @param geoX -- * @param geoY -- * @return if geodata exist -- */ -- public boolean hasGeoPos(int geoX, int geoY) -- { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // load all null blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { -- return false; -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[blockX + ix][blockY + iy] = _nullBlock; -+ } - } -- return region.hasGeo(); - } - -+ // GEODATA - GENERAL -+ - /** -- * Checks the specified position for available geodata. -- * @param x the world x -- * @param y the world y -- * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise -+ * Converts world X to geodata X. -+ * @param worldX -+ * @return int : Geo X - */ -- public boolean hasGeo(int x, int y) -+ public static int getGeoX(int worldX) - { -- return hasGeoPos(getGeoX(x), getGeoY(y)); -+ return (MathUtil.limit(worldX, World.MAP_MIN_X, World.MAP_MAX_X) - World.MAP_MIN_X) >> 4; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe check -+ * Converts world Y to geodata Y. -+ * @param worldY -+ * @return int : Geo Y - */ -- public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) -+ public static int getGeoY(int worldY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return true; -- } -- return region.checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(worldY, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.MAP_MIN_Y) >> 4; - } - - /** -+ * Converts geodata X to world X. - * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe anti-corner cut -+ * @return int : World X - */ -- public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) -+ public static int getWorldX(int geoX) - { -- boolean can = true; -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- return can && checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(geoX, 0, GeoStructure.GEO_CELLS_X) << 4) + World.MAP_MIN_X + 8; - } - - /** -- * @param geoX -+ * Converts geodata Y to world Y. - * @param geoY -- * @param worldZ -- * @return the nearest Z value -+ * @return int : World Y - */ -- public int getNearestZ(int geoX, int geoY, int worldZ) -+ public static int getWorldY(int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return worldZ; -- } -- return region.getNearestZ(geoX, geoY, worldZ); -+ return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next lower Z value -+ * Returns block of geodata on given coordinates. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return {@link ABlock} : Block of geodata. - */ -- public int getNextLowerZ(int geoX, int geoY, int worldZ) -+ private final ABlock getBlock(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final int x = geoX / GeoStructure.BLOCK_CELLS_X; -+ final int y = geoY / GeoStructure.BLOCK_CELLS_Y; -+ -+ // if x or y is out of array return null -+ if ((x > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) - { -- return worldZ; -+ return _blocks[x][y]; - } -- return region.getNextLowerZ(geoX, geoY, worldZ); -+ return null; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next higher Z value -+ * Check if geo coordinates has geo. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return boolean : True, if given geo coordinates have geodata - */ -- public int getNextHigherZ(int geoX, int geoY, int worldZ) -+ public boolean hasGeoPos(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final ABlock block = getBlock(geoX, geoY); -+ if (block == null) // null block check - { -- return worldZ; -+ // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) -+ // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); -+ return false; - } -- return region.getNextHigherZ(geoX, geoY, worldZ); -+ return block.hasGeoPos(); - } - - /** -- * Gets the Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the height of cell, which is closest to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, closest to given coordinates. - */ -- public int getHeight(int x, int y, int z) -+ public short getHeightNearest(int geoX, int geoY, int worldZ) - { -- return getNearestZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getHeightNearest(geoX, geoY, worldZ) : (short) worldZ; - } - - /** -- * Gets the next lower Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the NSWE flag byte of cell, which is closes to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. - */ -- public int getLowerHeight(int x, int y, int z) -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) - { -- return getNextLowerZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getNsweNearest(geoX, geoY, worldZ) : (byte) 0xFF; - } - - /** -- * Gets the next higher Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Check if world coordinates has geo. -+ * @param worldX : World X -+ * @param worldY : World Y -+ * @return boolean : True, if given world coordinates have geodata - */ -- public int getHigherHeight(int x, int y, int z) -+ public boolean hasGeo(int worldX, int worldY) - { -- return getNextHigherZ(getGeoX(x), getGeoY(y), z); -+ return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); - } - - /** -- * @param worldX -- * @return the geo X -+ * Returns closest Z coordinate according to geodata. -+ * @param worldX : world x -+ * @param worldY : world y -+ * @param worldZ : world z -+ * @return short : nearest Z coordinates according to geodata - */ -- public int getGeoX(int worldX) -+ public short getHeight(int worldX, int worldY, int worldZ) - { -- return (worldX - WORLD_MIN_X) / 16; -+ return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - -- /** -- * @param worldY -- * @return the geo Y -- */ -- public int getGeoY(int worldY) -- { -- return (worldY - WORLD_MIN_Y) / 16; -- } -+ // PATHFINDING - - /** -- * @param worldZ -- * @return the geo Z -+ * Check line of sight from {@link WorldObject} to {@link WorldObject}. -+ * @param origin : The origin object. -+ * @param target : The target object. -+ * @return {@code boolean} : True if origin can see target - */ -- public int getGeoZ(int worldZ) -+ public boolean canSeeTarget(WorldObject origin, WorldObject target) - { -- return (worldZ - WORLD_MIN_Z) / 16; -- } -- -- /** -- * @param geoX -- * @return the world X -- */ -- public int getWorldX(int geoX) -- { -- return (geoX * 16) + WORLD_MIN_X + 8; -- } -- -- /** -- * @param geoY -- * @return the world Y -- */ -- public int getWorldY(int geoY) -- { -- return (geoY * 16) + WORLD_MIN_Y + 8; -- } -- -- /** -- * @param geoZ -- * @return the world Z -- */ -- public int getWorldZ(int geoZ) -- { -- return (geoZ * 16) + WORLD_MIN_Z + 8; -- } -- -- /** -- * Can see target. Doors as target always return true. Checks doors between. -- * @param cha the character -- * @param target the target -- * @return {@code true} if the character can see the target (LOS), {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, WorldObject target) -- { -- if (target.isDoor()) -+ if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) - { -- // Can always see doors. - return true; - } -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), target.getX(), target.getY(), target.getZ(), target.getInstanceId()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param cha the character -- * @param worldPosition the world position -- * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) -- { -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instanceId -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @param tInstanceId the target's instanceId -- * @return -- */ -- public boolean canSeeTarget(int x, int y, int z, int instanceId, int tx, int ty, int tz, int tInstanceId) -- { -- return (instanceId != tInstanceId) ? false : canSeeTarget(x, y, z, instanceId, tx, ty, tz); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instanceId -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -- */ -- public boolean canSeeTarget(int x, int y, int z, int instanceId, int tx, int ty, int tz) -- { -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instanceId, true)) -+ -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = target.getX(); -+ final int ty = target.getY(); -+ final int tz = target.getZ(); -+ -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceId(), true)) - { - return false; - } -- return canSeeTarget(x, y, z, tx, ty, tz); -- } -- -- /** -- * @param prevX -- * @param prevY -- * @param prevGeoZ -- * @param curX -- * @param curY -- * @param nswe -- * @return the LOS Z value -- */ -- private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) -- { -- if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceId())) - { -- throw new RuntimeException("Multiple directions!"); -+ return false; - } - -- if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- return getNearestZ(curX, curY, prevGeoZ); -+ return true; - } - -- return getNextHigherZ(curX, curY, prevGeoZ); -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) -+ { -+ return true; -+ } -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) -+ { -+ return goz == gtz; -+ } -+ -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) -+ { -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight() * 2; -+ } -+ -+ double theight = 0; -+ if (target.isCreature()) -+ { -+ theight = ((Creature) target).getTemplate().getCollisionHeight() * 2; -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceId()); - } - - /** -- * Can see target. Does not check doors between. -- * @param xValue the x coordinate -- * @param yValue the y coordinate -- * @param zValue the z coordinate -- * @param txValue the target's x coordinate -- * @param tyValue the target's y coordinate -- * @param tzValue the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -+ * Check line of sight from {@link WorldObject} to {@link Location}. -+ * @param origin : The origin object. -+ * @param position : The target position. -+ * @return {@code boolean} : True if object can see position - */ -- public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) -+ public boolean canSeeTarget(WorldObject origin, Location position) - { -- int x = xValue; -- int y = yValue; -- int tx = txValue; -- int ty = tyValue; -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = position.getX(); -+ final int ty = position.getY(); -+ final int tz = position.getZ(); - -- int geoX = getGeoX(x); -- int geoY = getGeoY(y); -- int tGeoX = getGeoX(tx); -- int tGeoY = getGeoY(ty); -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceId(), true)) -+ { -+ return false; -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceId())) -+ { -+ return false; -+ } - -- int z = getNearestZ(geoX, geoY, zValue); -- int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- // Fastpath. -- if ((geoX == tGeoX) && (geoY == tGeoY)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- if (hasGeoPos(tGeoX, tGeoY)) -- { -- return z == tz; -- } - return true; - } - -- if (tz > z) -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) - { -- int tmp = tx; -- tx = x; -- x = tmp; -- -- tmp = ty; -- ty = y; -- y = tmp; -- -- tmp = tz; -- tz = z; -- z = tmp; -- -- tmp = tGeoX; -- tGeoX = geoX; -- geoX = tmp; -- -- tmp = tGeoY; -- tGeoY = geoY; -- geoY = tmp; -+ return goz == gtz; - } - -- final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); -- // First point is guaranteed to be available, skip it, we can always see our own position. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- final int prevZ = pointIter.z(); -- int prevGeoZ = prevZ; -- int ptIndex = 0; -- while (pointIter.next()) -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight(); -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceId()); -+ } -+ -+ /** -+ * Simple check for origin to target visibility. -+ * @param goxValue : origin X geodata coordinate -+ * @param goyValue : origin Y geodata coordinate -+ * @param gozValue : origin Z geodata coordinate -+ * @param oheight : origin height (if instance of {@link Character}) -+ * @param gtxValue : target X geodata coordinate -+ * @param gtyValue : target Y geodata coordinate -+ * @param gtzValue : target Z geodata coordinate -+ * @param theight : target height (if instance of {@link Character}) -+ * @param instanceId -+ * @return {@code boolean} : True, when target can be seen. -+ */ -+ private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, int instanceId) -+ { -+ int goz = gozValue; -+ int gtz = gtzValue; -+ int gox = goxValue; -+ int goy = goyValue; -+ int gtx = gtxValue; -+ int gty = gtyValue; -+ -+ // get line of sight Z coordinates -+ double losoz = goz + ((oheight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ double lostz = gtz + ((theight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ -+ // get X delta and signum -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirox = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; -+ final byte dirtx = sx > 0 ? GeoStructure.CELL_FLAG_W : GeoStructure.CELL_FLAG_E; -+ -+ // get Y delta and signum -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte diroy = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ final byte dirty = sy > 0 ? GeoStructure.CELL_FLAG_N : GeoStructure.CELL_FLAG_S; -+ -+ // get Z delta -+ final int dm = Math.max(dx, dy); -+ final double dz = (lostz - losoz) / dm; -+ -+ // get direction flag for diagonal movement -+ final byte diroxy = getDirXY(dirox, diroy); -+ final byte dirtxy = getDirXY(dirtx, dirty); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte diro; -+ byte dirt; -+ -+ // initialize node values -+ int nox = gox; -+ int noy = goy; -+ int ntx = gtx; -+ int nty = gty; -+ byte nsweo = getNsweNearest(gox, goy, goz); -+ byte nswet = getNsweNearest(gtx, gty, gtz); -+ -+ // loop -+ ABlock block; -+ int index; -+ for (int i = 0; i < ((dm + 1) / 2); i++) -+ { -+ // reset direction flag -+ diro = 0; -+ dirt = 0; - -- if ((curX == prevX) && (curY == prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- continue; -+ // calculate next point XY coordinates -+ d -= dy; -+ d += dx; -+ nox += sx; -+ ntx -= sx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroxy; -+ dirt |= dirtxy; - } -+ else if (e2 > -dy) -+ { -+ // calculate next point X coordinate -+ d -= dy; -+ nox += sx; -+ ntx -= sx; -+ diro |= dirox; -+ dirt |= dirtx; -+ } -+ else if (e2 < dx) -+ { -+ // calculate next point Y coordinate -+ d += dx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroy; -+ dirt |= dirty; -+ } - -- final int beeCurZ = pointIter.z(); -- int curGeoZ = prevGeoZ; -- -- // Check if the position has geodata. -- if (hasGeoPos(curX, curY)) - { -- final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); -- curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); -- int maxHeight; -- if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) -+ // get block of the next cell -+ block = getBlock(nox, noy); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nsweo & diro) == 0) - { -- maxHeight = z + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); - } - else - { -- maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); - } - -- boolean canSeeThrough = false; -- if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) -+ // layer does not exist, return -+ if (index == -1) - { -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else -- { -- canSeeThrough = true; -- } -+ return false; - } - -- if (!canSeeThrough) -+ // get layer and next line of sight Z coordinate -+ goz = block.getHeight(index); -+ losoz += dz; -+ -+ // perform line of sight check, return when fails -+ if ((goz - losoz) > Config.MAX_OBSTACLE_HEIGHT) - { - return false; - } -+ -+ // get layer nswe -+ nsweo = block.getNswe(index); - } -+ { -+ // get block of the next cell -+ block = getBlock(ntx, nty); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nswet & dirt) == 0) -+ { -+ index = block.getIndexAbove(ntx, nty, gtz - GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ else -+ { -+ index = block.getIndexBelow(ntx, nty, gtz + GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ -+ // layer does not exist, return -+ if (index == -1) -+ { -+ return false; -+ } -+ -+ // get layer and next line of sight Z coordinate -+ gtz = block.getHeight(index); -+ lostz -= dz; -+ -+ // perform line of sight check, return when fails -+ if ((gtz - lostz) > Config.MAX_OBSTACLE_HEIGHT) -+ { -+ return false; -+ } -+ -+ // get layer nswe -+ nswet = block.getNswe(index); -+ } - -- prevX = curX; -- prevY = curY; -- prevGeoZ = curGeoZ; -- ++ptIndex; -+ // update coords -+ gox = nox; -+ goy = noy; -+ gtx = ntx; -+ gty = nty; - } - -- return true; -+ // when iteration is completed, compare final Z coordinates -+ return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); - } - - /** -- * Move check. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param zValue the z coordinate -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tzValue the target's z coordinate -- * @param instanceId the instance id -- * @return the last Location (x,y,z) where player can walk - just before wall -+ * Check movement from coordinates to coordinates. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instanceId -+ * @return {code boolean} : True if target coordinates are reachable from origin coordinates - */ -- public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, int instanceId) -+ public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) - { -- final int geoX = getGeoX(x); -- final int geoY = getGeoY(y); -- final int z = getNearestZ(geoX, geoY, zValue); -- final int tGeoX = getGeoX(tx); -- final int tGeoY = getGeoY(ty); -- final int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instanceId, false)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } -- if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instanceId)) -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = z; -+ // perform geodata check -+ final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instanceId); -+ return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); -+ } -+ -+ /** -+ * Check movement from origin to target. Returns last available point in the checked path. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instanceId -+ * @return {@link Location} : Last point where object can walk (just before wall) -+ */ -+ public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) -+ { -+ // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instanceId, false)) -+ { -+ return new Location(ox, oy, oz); -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instanceId)) -+ { -+ return new Location(ox, oy, oz); -+ } - -- while (pointIter.next()) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -- -- if (hasGeoPos(prevX, prevY)) -- { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -- { -- // Can't move, return previous location. -- return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); -- } -- } -- -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return new Location(tx, ty, tz); - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != tz)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- // Different floors, return start location. -- return new Location(x, y, z); -+ return new Location(tx, ty, tz); - } - -- return new Location(tx, ty, tz); -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) -+ { -+ return new Location(tx, ty, tz); -+ } -+ -+ // perform geodata check -+ return checkMove(gox, goy, goz, gtx, gty, gtz, instanceId); - } - - /** -- * Checks if its possible to move from one location to another. -- * @param fromX the X coordinate to start checking from -- * @param fromY the Y coordinate to start checking from -- * @param fromZvalue the Z coordinate to start checking from -- * @param toX the X coordinate to end checking at -- * @param toY the Y coordinate to end checking at -- * @param toZvalue the Z coordinate to end checking at -- * @param instanceId the instance -- * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise -+ * With this method you can check if a position is visible or can be reached by beeline movement.
-+ * Target X and Y reachable and Z is on same floor: -+ *
    -+ *
  • Location of the target with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y reachable but Z is on another floor: -+ *
    -+ *
  • Location of the origin with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y not reachable: -+ *
    -+ *
  • Last accessible location in destination to target.
  • -+ *
-+ * @param gox : origin X geodata coordinate -+ * @param goy : origin Y geodata coordinate -+ * @param goz : origin Z geodata coordinate -+ * @param gtx : target X geodata coordinate -+ * @param gty : target Y geodata coordinate -+ * @param gtz : target Z geodata coordinate -+ * @param instanceId -+ * @return {@link GeoLocation} : The last allowed point of movement. - */ -- public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, int instanceId) -+ protected final GeoLocation checkMove(int gox, int goy, int goz, int gtx, int gty, int gtz, int instanceId) - { -- final int geoX = getGeoX(fromX); -- final int geoY = getGeoY(fromY); -- final int fromZ = getNearestZ(geoX, geoY, fromZvalue); -- final int tGeoX = getGeoX(toX); -- final int tGeoY = getGeoY(toY); -- final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); -- -- if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instanceId, false)) -+ if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instanceId, false)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } -- if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instanceId)) -+ if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instanceId)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = fromZ; -+ // get X delta, signum and direction flag -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirX = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; - -- while (pointIter.next()) -+ // get Y delta, signum and direction flag -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte dirY = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ -+ // get direction flag for diagonal movement -+ final byte dirXY = getDirXY(dirX, dirY); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte direction; -+ -+ // load pointer coordinates -+ int gpx = gox; -+ int gpy = goy; -+ int gpz = goz; -+ -+ // load next pointer -+ int nx = gpx; -+ int ny = gpy; -+ -+ // loop -+ int count = 0; -+ while (count++ < Config.MAX_ITERATIONS) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -+ direction = 0; - -- if (hasGeoPos(prevX, prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -+ d -= dy; -+ d += dx; -+ nx += sx; -+ ny += sy; -+ direction |= dirXY; -+ } -+ else if (e2 > -dy) -+ { -+ d -= dy; -+ nx += sx; -+ direction |= dirX; -+ } -+ else if (e2 < dx) -+ { -+ d += dx; -+ ny += sy; -+ direction |= dirY; -+ } -+ -+ // obstacle found, return -+ if ((getNsweNearest(gpx, gpy, gpz) & direction) == 0) -+ { -+ return new GeoLocation(gpx, gpy, gpz); -+ } -+ -+ // update pointer coordinates -+ gpx = nx; -+ gpy = ny; -+ gpz = getHeightNearest(nx, ny, gpz); -+ -+ // target coordinates reached -+ if ((gpx == gtx) && (gpy == gty)) -+ { -+ if (gpz == gtz) - { -- return false; -+ // path found, Z coordinates are okay, return target point -+ return new GeoLocation(gtx, gty, gtz); - } -+ -+ // path found, Z coordinates are not okay, return last good point -+ return new GeoLocation(gpx, gpy, gpz); - } -+ } -+ -+ return new GeoLocation(gox, goy, goz); -+ } -+ -+ /** -+ * Returns diagonal NSWE flag format of combined two NSWE flags. -+ * @param dirX : X direction NSWE flag -+ * @param dirY : Y direction NSWE flag -+ * @return byte : NSWE flag of combined direction -+ */ -+ private static byte getDirXY(byte dirX, byte dirY) -+ { -+ // check axis directions -+ if (dirY == GeoStructure.CELL_FLAG_N) -+ { -+ if (dirX == GeoStructure.CELL_FLAG_W) -+ { -+ return GeoStructure.CELL_FLAG_NW; -+ } - -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return GeoStructure.CELL_FLAG_NE; - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) -+ if (dirX == GeoStructure.CELL_FLAG_W) - { -- // Different floors. -- return false; -+ return GeoStructure.CELL_FLAG_SW; - } - -- return true; -+ return GeoStructure.CELL_FLAG_SE; - } - -+ /** -+ * Returns the list of location objects as a result of complete path calculation. -+ * @param ox : origin x -+ * @param oy : origin y -+ * @param oz : origin z -+ * @param tx : target x -+ * @param ty : target y -+ * @param tz : target z -+ * @param instanceId -+ * @return {@code List} : complete path from nodes -+ */ -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) -+ { -+ return null; -+ } -+ -+ /** -+ * Returns the instance of the {@link GeoEngine}. -+ * @return {@link GeoEngine} : The instance. -+ */ - public static GeoEngine getInstance() - { - return SingletonHolder.INSTANCE; -@@ -742,6 +933,6 @@ - - private static class SingletonHolder - { -- protected static final GeoEngine INSTANCE = new GeoEngine(); -+ protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); - } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (working copy) -@@ -20,87 +20,83 @@ - import java.util.LinkedList; - import java.util.List; - import java.util.ListIterator; --import java.util.logging.Level; --import java.util.logging.Logger; - - import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; --import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; --import org.l2jmobius.gameserver.model.World; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -+import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -+import org.l2jmobius.gameserver.model.Location; - - /** -- * @author -Nemesiss- -+ * @author Hasha - */ --public class GeoEnginePathfinding -+final class GeoEnginePathfinding extends GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); -+ // pre-allocated buffers -+ private final BufferHolder[] _buffers; - -- private BufferInfo[] _buffers; -- - protected GeoEnginePathfinding() - { -- try -+ super(); -+ -+ final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ _buffers = new BufferHolder[array.length]; -+ int count = 0; -+ for (int i = 0; i < array.length; i++) - { -- final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ final String buf = array[i]; -+ final String[] args = buf.split("x"); - -- _buffers = new BufferInfo[array.length]; -- -- String buf; -- String[] args; -- for (int i = 0; i < array.length; i++) -+ try - { -- buf = array[i]; -- args = buf.split("x"); -- if (args.length != 2) -- { -- throw new Exception("Invalid buffer definition: " + buf); -- } -- -- _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); -+ final int size = Integer.parseInt(args[1]); -+ count += size; -+ _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); - } -+ catch (Exception e) -+ { -+ LOGGER.warning("GeoEnginePathfinding: Can not load buffer setting: " + buf); -+ } - } -- catch (Exception e) -- { -- LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); -- throw new Error("CellPathFinding: load aborted"); -- } -+ -+ LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); - } - -- public boolean pathNodesExist(short regionoffset) -+ @Override -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) - { -- return false; -- } -- -- public List findPath(int x, int y, int z, int tx, int ty, int tz, int instanceId) -- { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -- if (!GeoEngine.getInstance().hasGeo(x, y)) -+ // get origin and check existing geo coords -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { - 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)) -+ -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coords -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { - 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)))); -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // Prepare buffer for pathfinding calculations -+ final NodeBuffer buffer = getBuffer(64 + (2 * Math.max(Math.abs(gox - gtx), Math.abs(goy - gty)))); - if (buffer == null) - { - return null; - } - -- List path = null; -+ // find path -+ List path = null; - try - { -- final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); -- -+ final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); - if (result == null) - { - return null; -@@ -124,33 +120,45 @@ - return path; - } - -- int currentX, currentY, currentZ; -- ListIterator middlePoint; -+ // get path list iterator -+ final ListIterator point = path.listIterator(); - -- middlePoint = path.listIterator(); -- currentX = x; -- currentY = y; -- currentZ = z; -+ // get node A (origin) -+ int nodeAx = gox; -+ int nodeAy = goy; -+ short nodeAz = goz; - -- while (middlePoint.hasNext()) -+ // get node B -+ GeoLocation nodeB = (GeoLocation) point.next(); -+ -+ // iterate thought the path to optimize it -+ int count = 0; -+ while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) - { -- final AbstractNodeLoc locMiddle = middlePoint.next(); -- if (!middlePoint.hasNext()) -- { -- break; -- } -+ // get node C -+ final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - -- final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); -- if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instanceId)) -+ // check movement from node A to node C -+ final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instanceId); -+ if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) - { -- middlePoint.remove(); -+ // can move from node A to node C -+ -+ // remove node B -+ point.remove(); - } - else - { -- currentX = locMiddle.getX(); -- currentY = locMiddle.getY(); -- currentZ = locMiddle.getZ(); -+ // can not move from node A to node C -+ -+ // set node A (node B is part of path, update A coordinates) -+ nodeAx = nodeB.getGeoX(); -+ nodeAy = nodeB.getGeoY(); -+ nodeAz = (short) nodeB.getZ(); - } -+ -+ // set node B -+ nodeB = (GeoLocation) point.next(); - } - - return path; -@@ -157,144 +165,102 @@ - } - - /** -- * Convert geodata position to pathnode position -- * @param geo_pos -- * @return pathnode position -+ * Create list of node locations as result of calculated buffer node tree. -+ * @param node : the entry point -+ * @return List : list of node location - */ -- public short getNodePos(int geo_pos) -+ private static List constructPath(Node node) - { -- return (short) (geo_pos >> 3); // OK? -- } -- -- /** -- * Convert node position to pathnode block position -- * @param node_pos -- * @return pathnode block position (0...255) -- */ -- public short getNodeBlock(int node_pos) -- { -- return (short) (node_pos % 256); -- } -- -- public byte getRegionX(int node_pos) -- { -- return (byte) ((node_pos >> 8) + World.TILE_X_MIN); -- } -- -- public byte getRegionY(int node_pos) -- { -- return (byte) ((node_pos >> 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 node_x rx -- * @return -- */ -- public int calculateWorldX(short node_x) -- { -- return World.MAP_MIN_X + (node_x * 128) + 48; -- } -- -- /** -- * Convert pathnode y to World y position -- * @param node_y -- * @return -- */ -- public int calculateWorldY(short node_y) -- { -- return World.MAP_MIN_Y + (node_y * 128) + 48; -- } -- -- private List constructPath(AbstractNode nodeValue) -- { -- final LinkedList path = new LinkedList<>(); -- int previousDirectionX = Integer.MIN_VALUE; -- int previousDirectionY = Integer.MIN_VALUE; -- int directionX, directionY; -+ // create empty list -+ final LinkedList list = new LinkedList<>(); - -- AbstractNode node = nodeValue; -- while (node.getParent() != null) -+ // set direction X/Y -+ int dx = 0; -+ int dy = 0; -+ -+ // get target parent -+ Node target = node; -+ Node parent = target.getParent(); -+ -+ // while parent exists -+ int count = 0; -+ while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { -- directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); -- directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); -+ // get parent <> target direction X/Y -+ final int nx = parent.getLoc().getGeoX() - target.getLoc().getGeoX(); -+ final int ny = parent.getLoc().getGeoY() - target.getLoc().getGeoY(); - -- // only add a new route point if moving direction changes -- if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) -+ // direction has changed? -+ if ((dx != nx) || (dy != ny)) - { -- previousDirectionX = directionX; -- previousDirectionY = directionY; -+ // add node to the beginning of the list -+ list.addFirst(target.getLoc()); - -- path.addFirst(node.getLoc()); -- node.setLoc(null); -+ // update direction X/Y -+ dx = nx; -+ dy = ny; - } - -- node = node.getParent(); -+ // move to next node, set target and get its parent -+ target = parent; -+ parent = target.getParent(); - } - -- return path; -+ // return list -+ return list; - } - -- private CellNodeBuffer alloc(int size) -+ /** -+ * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer. -+ * @param size : pre-calculated minimal required size -+ * @return NodeBuffer : buffer -+ */ -+ private final NodeBuffer getBuffer(int size) - { -- CellNodeBuffer current = null; -- for (BufferInfo i : _buffers) -+ NodeBuffer current = null; -+ for (BufferHolder holder : _buffers) - { -- if (i.mapSize >= size) -+ // Find proper size of buffer -+ if (holder._size < size) - { -- for (CellNodeBuffer buf : i.buffer) -+ continue; -+ } -+ -+ // Find unlocked NodeBuffer -+ for (NodeBuffer buffer : holder._buffer) -+ { -+ if (!buffer.isLocked()) - { -- if (buf.lock()) -- { -- current = buf; -- break; -- } -+ continue; - } -- if (current != null) -- { -- break; -- } - -- // not found, allocate temporary buffer -- current = new CellNodeBuffer(i.mapSize); -- current.lock(); -- if (i.buffer.size() < i.count) -- { -- i.buffer.add(current); -- break; -- } -+ return buffer; - } -+ -+ // NodeBuffer not found, allocate temporary buffer -+ current = new NodeBuffer(holder._size); -+ current.isLocked(); - } - - return current; - } - -- private static final class BufferInfo -+ /** -+ * NodeBuffer container with specified size and count of separate buffers. -+ */ -+ private static final class BufferHolder - { -- final int mapSize; -- final int count; -- ArrayList buffer; -+ final int _size; -+ List _buffer; - -- public BufferInfo(int size, int cnt) -+ public BufferHolder(int size, int count) - { -- mapSize = size; -- count = cnt; -- buffer = new ArrayList<>(count); -+ _size = size; -+ _buffer = new ArrayList<>(count); -+ for (int i = 0; i < count; i++) -+ { -+ _buffer.add(new NodeBuffer(size)); -+ } - } - } -- -- public static GeoEnginePathfinding getInstance() -- { -- return SingletonHolder.INSTANCE; -- } -- -- private static class SingletonHolder -- { -- protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); -- } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (working copy) -@@ -0,0 +1,197 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @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 height of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getHeightNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first above given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, above given coordinates. -+ */ -+ public abstract short getHeightAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first below given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is closest to given coordinates. -+ * @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 the NSWE flag byte of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getNsweNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first above given coordinates. -+ * @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 getNsweAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first below given coordinates. -+ * @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 getNsweBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is closes layer to given coordinates. -+ * @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. -+ * @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 above given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is first layer below given coordinates. -+ * @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 index to data of the cell, which is first layer below given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexBelowOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeight(int index); -+ -+ /** -+ * Returns the height of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightOriginal(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNswe(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNsweOriginal(int index); -+ -+ /** -+ * Sets the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @param nswe : New NSWE flag byte. -+ */ -+ public abstract void setNswe(int index, byte nswe); -+ -+ /** -+ * Saves the block in L2D format to {@link BufferedOutputStream}. Used only for L2D geodata conversion. -+ * @param stream : The stream. -+ * @throws IOException : Can't save the block to steam. -+ */ -+ public abstract void saveBlock(BufferedOutputStream stream) throws IOException; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (working copy) -@@ -0,0 +1,252 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockComplex(ByteBuffer bb, GeoFormat format) -+ { -+ // initialize buffer -+ _buffer = new byte[GeoStructure.BLOCK_CELLS * 3]; -+ -+ // load data -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // 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); -+ } -+ else -+ { -+ // get nswe -+ final byte nswe = bb.get(); -+ _buffer[i * 3] = nswe; -+ -+ // get height -+ final short height = bb.getShort(); -+ _buffer[(i * 3) + 1] = (byte) (height & 0x00FF); -+ _buffer[(i * 3) + 2] = (byte) (height >> 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height > worldZ ? height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height < worldZ ? height : Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public 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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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 int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_COMPLEX_L2D); -+ -+ // write block data -+ stream.write(_buffer, 0, GeoStructure.BLOCK_CELLS * 3); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (working copy) -@@ -0,0 +1,176 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockFlat extends ABlock -+{ -+ protected final short _height; -+ protected byte _nswe; -+ -+ /** -+ * Creates FlatBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockFlat(ByteBuffer bb, GeoFormat format) -+ { -+ _height = bb.getShort(); -+ _nswe = format != GeoFormat.L2D ? 0x0F : (byte) (0xFF); -+ if (format == GeoFormat.L2OFF) -+ { -+ bb.getShort(); -+ } -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return true; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height > worldZ ? _height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height < worldZ ? _height : Short.MAX_VALUE; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height > worldZ ? _nswe : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height < worldZ ? _nswe : 0; -+ } -+ -+ @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return index -+ return _height < worldZ ? 0 : -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _nswe = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_FLAT_L2D); -+ -+ // write height -+ stream.write((byte) (_height & 0x00FF)); -+ stream.write((byte) (_height >> 8)); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (working copy) -@@ -0,0 +1,465 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+import java.nio.ByteOrder; -+import java.util.Arrays; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockMultilayer extends ABlock -+{ -+ private static final int MAX_LAYERS = Byte.MAX_VALUE; -+ -+ private static ByteBuffer _temp; -+ -+ /** -+ * Initializes the temporarily buffer. -+ */ -+ public static void initialize() -+ { -+ // initialize temporarily buffer and sorting mechanism -+ _temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3); -+ _temp.order(ByteOrder.LITTLE_ENDIAN); -+ } -+ -+ /** -+ * Releases temporarily buffer. -+ */ -+ public static void release() -+ { -+ _temp = null; -+ } -+ -+ protected byte[] _buffer; -+ -+ /** -+ * Implicit constructor for children class. -+ */ -+ protected BlockMultilayer() -+ { -+ _buffer = null; -+ } -+ -+ /** -+ * Creates MultilayerBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockMultilayer(ByteBuffer bb, GeoFormat format) -+ { -+ // 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 = format != GeoFormat.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++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // get data -+ final short data = bb.getShort(); -+ -+ // add nswe and height -+ _temp.put((byte) (data & 0x000F)); -+ _temp.putShort((short) ((short) (data & 0xFFF0) >> 1)); -+ } -+ else -+ { -+ // add nswe -+ _temp.put(bb.get()); -+ -+ // add height -+ _temp.putShort(bb.getShort()); -+ } -+ } -+ } -+ -+ // 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 height -+ if (height > worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, return minimum value -+ return Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 height -+ if (height < worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, return maximum value -+ return Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 nswe -+ if (height > worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 nswe -+ if (height < worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @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 first layer data (first from bottom) -+ byte layers = _buffer[index++]; -+ -+ // loop though all cell layers, find closest layer -+ 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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ // get height -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(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]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ // get nswe -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ // set nswe -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_MULTILAYER_L2D); -+ -+ // for each cell -+ int index = 0; -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ // write layers count -+ final byte layers = _buffer[index++]; -+ stream.write(layers); -+ -+ // write cell data -+ stream.write(_buffer, index, layers * 3); -+ -+ // move index to next cell -+ index += layers * 3; -+ } -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (working copy) -@@ -0,0 +1,150 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockNull extends ABlock -+{ -+ private final byte _nswe; -+ -+ public BlockNull() -+ { -+ _nswe = (byte) 0xFF; -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return false; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweBelow(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) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) -+ { -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (nonexistent) -@@ -1,48 +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 . -- */ --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() -- { -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (nonexistent) -@@ -1,77 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (nonexistent) -@@ -1,56 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (working copy) -@@ -0,0 +1,39 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public enum GeoFormat -+{ -+ L2J("%d_%d.l2j"), -+ L2OFF("%d_%d_conv.dat"), -+ L2D("%d_%d.l2d"); -+ -+ private final String _filename; -+ -+ private GeoFormat(String filename) -+ { -+ _filename = filename; -+ } -+ -+ public String getFilename() -+ { -+ return _filename; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (working copy) -@@ -0,0 +1,67 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.geoengine.GeoEngine; -+import org.l2jmobius.gameserver.model.Location; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoLocation extends Location -+{ -+ private byte _nswe; -+ -+ public GeoLocation(int x, int y, int z) -+ { -+ super(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public void set(int x, int y, short z) -+ { -+ super.setXYZ(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public int getGeoX() -+ { -+ return _x; -+ } -+ -+ public int getGeoY() -+ { -+ return _y; -+ } -+ -+ @Override -+ public int getX() -+ { -+ return GeoEngine.getWorldX(_x); -+ } -+ -+ @Override -+ public int getY() -+ { -+ return GeoEngine.getWorldY(_y); -+ } -+ -+ public byte getNSWE() -+ { -+ return _nswe; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (working copy) -@@ -0,0 +1,70 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoStructure -+{ -+ // cells -+ public static final byte CELL_FLAG_E = 1 << 0; -+ public static final byte CELL_FLAG_W = 1 << 1; -+ public static final byte CELL_FLAG_S = 1 << 2; -+ public static final byte CELL_FLAG_N = 1 << 3; -+ public static final byte CELL_FLAG_SE = 1 << 4; -+ public static final byte CELL_FLAG_SW = 1 << 5; -+ public static final byte CELL_FLAG_NE = 1 << 6; -+ public static final byte CELL_FLAG_NW = (byte) (1 << 7); -+ -+ public static final int CELL_HEIGHT = 8; -+ public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6; -+ -+ // blocks -+ public static final byte TYPE_FLAT_L2J_L2OFF = 0; -+ public static final byte TYPE_FLAT_L2D = (byte) 0xD0; -+ public static final byte TYPE_COMPLEX_L2J = 1; -+ public static final byte TYPE_COMPLEX_L2OFF = 0x40; -+ public static final byte TYPE_COMPLEX_L2D = (byte) 0xD1; -+ 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) -+ public static final byte TYPE_MULTILAYER_L2D = (byte) 0xD2; -+ -+ 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; -+ -+ // regions -+ 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; -+ -+ // global geodata -+ private static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); -+ private 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; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (nonexistent) -@@ -1,42 +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 . -- */ --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); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (working copy) -@@ -0,0 +1,53 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public interface IGeoObject -+{ -+ /** -+ * Returns geodata X coordinate of the {@link IGeoObject}. -+ * @return int : Geodata X coordinate. -+ */ -+ int getGeoX(); -+ -+ /** -+ * Returns geodata Y coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Y coordinate. -+ */ -+ int getGeoY(); -+ -+ /** -+ * Returns geodata Z coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Z coordinate. -+ */ -+ int getGeoZ(); -+ -+ /** -+ * Returns height of the {@link IGeoObject}. -+ * @return int : Height. -+ */ -+ int getHeight(); -+ -+ /** -+ * Returns {@link IGeoObject} data. -+ * @return byte[][] : {@link IGeoObject} data. -+ */ -+ byte[][] getObjectGeoData(); -+} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (nonexistent) -@@ -1,47 +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 . -- */ --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(); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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); -- -- // 1 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (nonexistent) -@@ -1,55 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --/** -- * @author HorridoJoho -- */ --public final class NullRegion implements IRegion --{ -- public static final NullRegion INSTANCE = new NullRegion(); -- -- @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 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Region.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (nonexistent) -@@ -1,92 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (nonexistent) -@@ -1,87 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public abstract class AbstractNode --{ -- private T _loc; -- private AbstractNode _parent; -- -- public AbstractNode(T loc) -- { -- _loc = loc; -- } -- -- public void setParent(AbstractNode p) -- { -- _parent = p; -- } -- -- public AbstractNode getParent() -- { -- return _parent; -- } -- -- public T getLoc() -- { -- return _loc; -- } -- -- public void setLoc(T l) -- { -- _loc = l; -- } -- -- @Override -- public int hashCode() -- { -- final int prime = 31; -- int result = 1; -- result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); -- return result; -- } -- -- @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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (nonexistent) -@@ -1,33 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --/** -- * @author -Nemesiss- -- */ --public abstract class AbstractNodeLoc --{ -- public abstract int getX(); -- -- public abstract int getY(); -- -- public abstract int getZ(); -- -- public abstract int getNodeX(); -- -- public abstract int getNodeY(); --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (nonexistent) -@@ -1,67 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public class CellNode extends AbstractNode --{ -- 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (nonexistent) -@@ -1,348 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --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 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) -- { -- _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(); -- } -- -- public final List debugPath() -- { -- final List 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 void getNeighbors() -- { -- if (!_current.getLoc().canGoAll()) -- { -- 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); -- } -- -- // SouthEast -- if ((nodeE != null) && (nodeS != null)) -- { -- if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) -- { -- addNode(x + 1, y + 1, z, true); -- } -- } -- -- // SouthWest -- if ((nodeS != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) -- { -- addNode(x - 1, y + 1, z, true); -- } -- } -- -- // NorthEast -- if ((nodeN != null) && (nodeE != null)) -- { -- if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) -- { -- addNode(x + 1, y - 1, z, true); -- } -- } -- -- // NorthWest -- if ((nodeN != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) -- { -- addNode(x - 1, y - 1, z, true); -- } -- } -- } -- -- private 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(); -- // reinit node if needed -- if (result.getLoc() != null) -- { -- result.getLoc().set(x, y, z); -- } -- else -- { -- result.setLoc(new NodeLoc(x, y, z)); -- } -- } -- -- return result; -- } -- -- private 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 boolean isHighWeight(int x, int y, int z) -- { -- final CellNode result = getNode(x, y, z); -- if (result == null) -- { -- return true; -- } -- -- if (!result.getLoc().canGoAll()) -- { -- return true; -- } -- if (Math.abs(result.getLoc().getZ() - z) > 16) -- { -- return true; -- } -- -- return false; -- } -- -- private 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (working copy) -@@ -0,0 +1,87 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+ -+/** -+ * @author Hasha -+ */ -+public class Node -+{ -+ // node coords and nswe flag -+ private GeoLocation _loc; -+ -+ // node parent (for reverse path construction) -+ private Node _parent; -+ // node child (for moving over nodes during iteration) -+ private Node _child; -+ -+ // node G cost (movement cost = parent movement cost + current movement cost) -+ private double _cost = -1000; -+ -+ public void setLoc(int x, int y, int z) -+ { -+ _loc = new GeoLocation(x, y, z); -+ } -+ -+ public GeoLocation getLoc() -+ { -+ return _loc; -+ } -+ -+ public void setParent(Node parent) -+ { -+ _parent = parent; -+ } -+ -+ public Node getParent() -+ { -+ return _parent; -+ } -+ -+ public void setChild(Node child) -+ { -+ _child = child; -+ } -+ -+ public Node getChild() -+ { -+ return _child; -+ } -+ -+ public void setCost(double cost) -+ { -+ _cost = cost; -+ } -+ -+ public double getCost() -+ { -+ return _cost; -+ } -+ -+ public void free() -+ { -+ // reset node location -+ _loc = null; -+ -+ // reset node parent, child and cost -+ _parent = null; -+ _child = null; -+ _cost = -1000; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (working copy) -@@ -0,0 +1,306 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import java.util.concurrent.locks.ReentrantLock; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+ -+/** -+ * @author DS, Hasha; Credits to Diamond -+ */ -+public class NodeBuffer -+{ -+ private final ReentrantLock _lock = new ReentrantLock(); -+ private final int _size; -+ private final Node[][] _buffer; -+ -+ // center coordinates -+ private int _cx = 0; -+ private int _cy = 0; -+ -+ // target coordinates -+ private int _gtx = 0; -+ private int _gty = 0; -+ private short _gtz = 0; -+ -+ private Node _current = null; -+ -+ /** -+ * Constructor of NodeBuffer. -+ * @param size : one dimension size of buffer -+ */ -+ public NodeBuffer(int size) -+ { -+ // set size -+ _size = size; -+ -+ // initialize buffer -+ _buffer = new Node[size][size]; -+ for (int x = 0; x < size; x++) -+ { -+ for (int y = 0; y < size; y++) -+ { -+ _buffer[x][y] = 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 Node : first node of path -+ */ -+ public Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) -+ { -+ // set coordinates (middle of the line (gox,goy) - (gtx,gty), will be in the center of the buffer) -+ _cx = gox + ((gtx - gox - _size) / 2); -+ _cy = goy + ((gty - goy - _size) / 2); -+ -+ _gtx = gtx; -+ _gty = gty; -+ _gtz = gtz; -+ -+ _current = getNode(gox, goy, goz); -+ _current.setCost(getCostH(gox, goy, goz)); -+ -+ int count = 0; -+ do -+ { -+ // reached target? -+ if ((_current.getLoc().getGeoX() == _gtx) && (_current.getLoc().getGeoY() == _gty) && (Math.abs(_current.getLoc().getZ() - _gtz) < 8)) -+ { -+ return _current; -+ } -+ -+ // expand current node -+ expand(); -+ -+ // move pointer -+ _current = _current.getChild(); -+ } -+ while ((_current != null) && (count++ < Config.MAX_ITERATIONS)); -+ -+ return null; -+ } -+ -+ public boolean isLocked() -+ { -+ return _lock.tryLock(); -+ } -+ -+ public void free() -+ { -+ _current = null; -+ -+ for (Node[] nodes : _buffer) -+ { -+ for (Node node : nodes) -+ { -+ if (node.getLoc() != null) -+ { -+ node.free(); -+ } -+ } -+ } -+ -+ _lock.unlock(); -+ } -+ -+ /** -+ * Check _current Node and add its neighbors to the buffer. -+ */ -+ private final void expand() -+ { -+ // can't move anywhere, don't expand -+ final byte nswe = _current.getLoc().getNSWE(); -+ if (nswe == 0) -+ { -+ return; -+ } -+ -+ // get geo coords of the node to be expanded -+ final int x = _current.getLoc().getGeoX(); -+ final int y = _current.getLoc().getGeoY(); -+ final short z = (short) _current.getLoc().getZ(); -+ -+ // can move north, expand -+ if ((nswe & GeoStructure.CELL_FLAG_N) != 0) -+ { -+ addNode(x, y - 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move south, expand -+ if ((nswe & GeoStructure.CELL_FLAG_S) != 0) -+ { -+ addNode(x, y + 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_W) != 0) -+ { -+ addNode(x - 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_E) != 0) -+ { -+ addNode(x + 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move north-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NW) != 0) -+ { -+ addNode(x - 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move north-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NE) != 0) -+ { -+ addNode(x + 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SW) != 0) -+ { -+ addNode(x - 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SE) != 0) -+ { -+ addNode(x + 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ } -+ -+ /** -+ * Returns node, if it exists in buffer. -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param z : node Z coord -+ * @return Node : node, if exits in buffer -+ */ -+ private final Node getNode(int x, int y, short z) -+ { -+ // check node X out of coordinates -+ final int ix = x - _cx; -+ if ((ix < 0) || (ix >= _size)) -+ { -+ return null; -+ } -+ -+ // check node Y out of coordinates -+ final int iy = y - _cy; -+ if ((iy < 0) || (iy >= _size)) -+ { -+ return null; -+ } -+ -+ // get node -+ final Node result = _buffer[ix][iy]; -+ -+ // check and update -+ if (result.getLoc() == null) -+ { -+ result.setLoc(x, y, z); -+ } -+ -+ // return node -+ return result; -+ } -+ -+ /** -+ * Add node given by coordinates to the buffer. -+ * @param x : geo X coord -+ * @param y : geo Y coord -+ * @param z : geo Z coord -+ * @param weight : weight of movement to new node -+ */ -+ private final void addNode(int x, int y, short z, int weight) -+ { -+ // get node to be expanded -+ final Node node = getNode(x, y, z); -+ if (node == null) -+ { -+ return; -+ } -+ -+ // Z distance between nearby cells is higher than cell size -+ if (node.getLoc().getZ() > (z + (2 * GeoStructure.CELL_HEIGHT))) -+ { -+ return; -+ } -+ -+ // node was already expanded, return -+ if (node.getCost() >= 0) -+ { -+ return; -+ } -+ -+ node.setParent(_current); -+ if (node.getLoc().getNSWE() != (byte) 0xFF) -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + (weight * Config.OBSTACLE_MULTIPLIER)); -+ } -+ else -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + weight); -+ } -+ -+ Node current = _current; -+ int count = 0; -+ while ((current.getChild() != null) && (count < (Config.MAX_ITERATIONS * 4))) -+ { -+ count++; -+ if (current.getChild().getCost() > node.getCost()) -+ { -+ node.setChild(current.getChild()); -+ break; -+ } -+ current = current.getChild(); -+ } -+ -+ if (count >= (Config.MAX_ITERATIONS * 4)) -+ { -+ System.err.println("Pathfinding: too long loop detected, cost:" + node.getCost()); -+ } -+ -+ current.setChild(node); -+ } -+ -+ /** -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param i : node Z coord -+ * @return double : node cost -+ */ -+ private final double getCostH(int x, int y, int i) -+ { -+ final int dX = x - _gtx; -+ final int dY = y - _gty; -+ final int dZ = (i - _gtz) / GeoStructure.CELL_HEIGHT; -+ -+ // return (Math.abs(dX) + Math.abs(dY) + Math.abs(dZ)) * Config.HEURISTIC_WEIGHT; // Manhattan distance -+ return Math.sqrt((dX * dX) + (dY * dY) + (dZ * dZ)) * Config.HEURISTIC_WEIGHT; // Direct distance -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -- --/** -- * @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 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 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; -- // return super.hashCode(); -- } -- -- @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; -- if (_x != other._x) -- { -- return false; -- } -- if (_y != other._y) -- { -- return false; -- } -- if (_goNorth != other._goNorth) -- { -- return false; -- } -- if (_goEast != other._goEast) -- { -- return false; -- } -- if (_goSouth != other._goSouth) -- { -- return false; -- } -- if (_goWest != other._goWest) -- { -- return false; -- } -- if (_geoHeight != other._geoHeight) -- { -- return false; -- } -- return true; -- } --} -Index: java/org/l2jmobius/gameserver/model/actor/Creature.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/Creature.java (revision 8399) -+++ java/org/l2jmobius/gameserver/model/actor/Creature.java (working copy) -@@ -57,8 +57,6 @@ - import org.l2jmobius.gameserver.enums.Team; - import org.l2jmobius.gameserver.enums.TeleportWhereType; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; - import org.l2jmobius.gameserver.instancemanager.IdManager; - import org.l2jmobius.gameserver.instancemanager.InstanceManager; - import org.l2jmobius.gameserver.instancemanager.MapRegionManager; -@@ -3362,7 +3360,7 @@ - - public boolean disregardingGeodata; - public int onGeodataPathIndex; -- public List geoPath; -+ public List geoPath; - public int geoPathAccurateTx; - public int geoPathAccurateTy; - public int geoPathGtx; -@@ -4386,7 +4384,7 @@ - if (((originalDistance - distance) > 30) && !isAfraid() && !isInVehicle) - { - // Path calculation -- overrides previous movement check -- m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId()); -+ m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId()); - if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found - { - if (isPlayer() && !_isFlying && !isInWater) -Index: java/org/l2jmobius/gameserver/model/actor/Summon.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/Summon.java (revision 8319) -+++ java/org/l2jmobius/gameserver/model/actor/Summon.java (working copy) -@@ -29,7 +29,6 @@ - import org.l2jmobius.gameserver.enums.ShotType; - import org.l2jmobius.gameserver.enums.Team; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; - import org.l2jmobius.gameserver.handler.IItemHandler; - import org.l2jmobius.gameserver.handler.ItemHandler; - import org.l2jmobius.gameserver.instancemanager.TerritoryWarManager; -@@ -646,7 +645,7 @@ - return false; - } - -- if ((this != target) && skill.isPhysical() && Config.PATHFINDING && (GeoEnginePathfinding.getInstance().findPath(getX(), getY(), getZ(), target.getX(), target.getY(), target.getZ(), getInstanceId()) == null)) -+ if ((this != target) && skill.isPhysical() && Config.PATHFINDING && (GeoEngine.getInstance().findPath(getX(), getY(), getZ(), target.getX(), target.getY(), target.getZ(), getInstanceId()) == null)) - { - sendPacket(SystemMessageId.CANNOT_SEE_TARGET); - return false; -Index: java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (revision 8352) -+++ java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (working copy) -@@ -13406,7 +13406,7 @@ - { - if (Config.CORRECT_PLAYER_Z) - { -- final int nearestZ = GeoEngine.getInstance().getHigherHeight(getX(), getY(), getZ()); -+ final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); - if (getZ() < nearestZ) - { - teleToLocation(new Location(getX(), getY(), nearestZ)); -Index: java/org/l2jmobius/gameserver/util/GeoUtils.java -=================================================================== ---- java/org/l2jmobius/gameserver/util/GeoUtils.java (revision 8319) -+++ java/org/l2jmobius/gameserver/util/GeoUtils.java (working copy) -@@ -19,7 +19,7 @@ - import java.awt.Color; - - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; - import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; - -@@ -26,25 +26,25 @@ - /** - * @author HorridoJoho - */ --public final class GeoUtils -+public class GeoUtils - { - public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); - - final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); - - while (iter.next()) - { -- final int wx = GeoEngine.getInstance().getWorldX(iter.x()); -- final int wy = GeoEngine.getInstance().getWorldY(iter.y()); -+ final int wx = GeoEngine.getWorldX(iter.x()); -+ final int wy = GeoEngine.getWorldY(iter.y()); - - prim.addPoint(Color.RED, wx, wy, z); - } -@@ -53,21 +53,21 @@ - - public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); -- int wy = GeoEngine.getInstance().getWorldY(prevY); -+ int wx = GeoEngine.getWorldX(prevX); -+ int wy = GeoEngine.getWorldY(prevY); - int wz = iter.z(); - prim.addPoint(Color.RED, wx, wy, wz); - -@@ -78,8 +78,8 @@ - - if ((curX != prevX) || (curY != prevY)) - { -- wx = GeoEngine.getInstance().getWorldX(curX); -- wy = GeoEngine.getInstance().getWorldY(curY); -+ wx = GeoEngine.getWorldX(curX); -+ wy = GeoEngine.getWorldY(curY); - wz = iter.z(); - - prim.addPoint(Color.RED, wx, wy, wz); -@@ -93,7 +93,7 @@ - - private static Color getDirectionColor(int x, int y, int z, int nswe) - { -- if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) -+ if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) - { - return Color.GREEN; - } -@@ -109,9 +109,8 @@ - int iPacket = 0; - - ExServerPrimitive exsp = null; -- final GeoEngine ge = GeoEngine.getInstance(); -- final int playerGx = ge.getGeoX(player.getX()); -- final int playerGy = ge.getGeoY(player.getY()); -+ final int playerGx = GeoEngine.getGeoX(player.getX()); -+ final int playerGy = GeoEngine.getGeoY(player.getY()); - for (int dx = -geoRadius; dx <= geoRadius; ++dx) - { - for (int dy = -geoRadius; dy <= geoRadius; ++dy) -@@ -135,12 +134,12 @@ - final int gx = playerGx + dx; - final int gy = playerGy + dy; - -- final int x = ge.getWorldX(gx); -- final int y = ge.getWorldY(gy); -- final int z = ge.getNearestZ(gx, gy, player.getZ()); -+ final int x = GeoEngine.getWorldX(gx); -+ final int y = GeoEngine.getWorldY(gy); -+ final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); - - // north arrow -- Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); -+ Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); - 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); -@@ -147,7 +146,7 @@ - exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z); - - // east arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); - 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); -@@ -154,13 +153,13 @@ - exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z); - - // south arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); - 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, Cell.NSWE_WEST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); - 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); -@@ -188,15 +187,15 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_EAST; -+ return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_EAST; -+ return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; - } - else - { -- return Cell.NSWE_EAST; -+ return GeoStructure.CELL_FLAG_E; // Direction.EAST; - } - } - else if (x < lastX) // west -@@ -203,26 +202,27 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_WEST; -+ return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_WEST; -+ return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; - } - else - { -- return Cell.NSWE_WEST; -+ return GeoStructure.CELL_FLAG_W; // Direction.WEST; - } - } -- else // unchanged x -+ else -+ // unchanged x - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH; -+ return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH; -+ return GeoStructure.CELL_FLAG_N; // Direction.NORTH; - } - else - { -Index: java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java -=================================================================== ---- java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (nonexistent) -+++ java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (working copy) -@@ -0,0 +1,386 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.tools.geodataconverter; -+ -+import java.io.BufferedOutputStream; -+import java.io.File; -+import java.io.FileOutputStream; -+import java.io.RandomAccessFile; -+import java.nio.ByteOrder; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.Scanner; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.commons.util.PropertiesParser; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoDataConverter -+{ -+ private static GeoFormat _format; -+ private static ABlock[][] _blocks; -+ -+ public static void main(String[] args) -+ { -+ // initialize config -+ loadGeoengineConfigs(); -+ -+ // get geodata format -+ String type = ""; -+ try (Scanner scn = new Scanner(System.in)) -+ { -+ while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) -+ { -+ System.out.println("GeoDataConverter: Select source geodata type:"); -+ System.out.println(" J: L2J (e.g. 23_22.l2j)"); -+ System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); -+ System.out.println(" E: Exit"); -+ System.out.print("Choice: "); -+ type = scn.next(); -+ } -+ } -+ if (type.equalsIgnoreCase("E")) -+ { -+ System.exit(0); -+ } -+ -+ _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; -+ -+ // start conversion -+ System.out.println("GeoDataConverter: Converting all " + _format + " files."); -+ -+ // initialize geodata container -+ _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files -+ int converted = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) -+ { -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) -+ { -+ final String input = String.format(_format.getFilename(), rx, ry); -+ final String filepath = Config.GEODATA_PATH; -+ final File f = new File(filepath + input); -+ if (f.exists() && !f.isDirectory()) -+ { -+ // load geodata -+ if (!loadGeoBlocks(input)) -+ { -+ System.out.println("GeoDataConverter: Unable to load " + input + " region file."); -+ continue; -+ } -+ -+ // recalculate nswe -+ if (!recalculateNswe()) -+ { -+ System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); -+ continue; -+ } -+ -+ // save geodata -+ final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); -+ if (!saveGeoBlocks(output)) -+ { -+ System.out.println("GeoDataConverter: Unable to save " + output + " region file."); -+ continue; -+ } -+ -+ converted++; -+ System.out.println("GeoDataConverter: Created " + output + " region file."); -+ } -+ } -+ } -+ System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); -+ } -+ -+ /** -+ * Loads geo blocks from buffer of the region file. -+ * @param filename : The name of the to load. -+ * @return boolean : True when successful. -+ */ -+ private static boolean loadGeoBlocks(String filename) -+ { -+ // region file is load-able, try to load it -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) -+ { -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) -+ if (_format == GeoFormat.L2OFF) -+ { -+ for (int i = 0; i < 18; i++) -+ { -+ buffer.get(); -+ } -+ } -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ if (_format == GeoFormat.L2J) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2J: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2J: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ else -+ { -+ // get block type -+ final short type = buffer.getShort(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ default: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ if (buffer.remaining() > 0) -+ { -+ System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ return false; -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); -+ return false; -+ } -+ } -+ -+ /** -+ * Recalculate diagonal flags for the region file. -+ * @return boolean : True when successful. -+ */ -+ private static boolean recalculateNswe() -+ { -+ try -+ { -+ for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) -+ { -+ for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) -+ { -+ // get block -+ final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // skip flat blocks -+ if (block instanceof BlockFlat) -+ { -+ continue; -+ } -+ -+ // for complex and multilayer blocks go though all layers -+ short height = Short.MAX_VALUE; -+ int index; -+ while ((index = block.getIndexBelow(x, y, height)) != -1) -+ { -+ // get height and nswe -+ height = block.getHeight(index); -+ byte nswe = block.getNswe(index); -+ -+ // update nswe with diagonal flags -+ nswe = updateNsweBelow(x, y, height, nswe); -+ -+ // set nswe of the cell -+ block.setNswe(index, nswe); -+ } -+ } -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ /** -+ * Updates the NSWE flag with diagonal flags. -+ * @param x : Geodata X coordinate. -+ * @param y : Geodata Y coordinate. -+ * @param z : Geodata Z coordinate. -+ * @param nsweValue : NSWE flag to be updated. -+ * @return byte : Updated NSWE flag. -+ */ -+ private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) -+ { -+ byte nswe = nsweValue; -+ -+ // calculate virtual layer height -+ final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); -+ -+ // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) -+ final byte nsweN = getNsweBelow(x, y - 1, height); -+ final byte nsweS = getNsweBelow(x, y + 1, height); -+ final byte nsweW = getNsweBelow(x - 1, y, height); -+ final byte nsweE = getNsweBelow(x + 1, y, height); -+ -+ // north-west -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NW; -+ } -+ -+ // north-east -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NE; -+ } -+ -+ // south-west -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SW; -+ } -+ -+ // south-east -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SE; -+ } -+ -+ return nswe; -+ } -+ -+ private static byte getNsweBelow(int geoX, int geoY, short worldZ) -+ { -+ // out of geo coordinates -+ if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) -+ { -+ return 0; -+ } -+ -+ // out of geo coordinates -+ if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) -+ { -+ return 0; -+ } -+ -+ // get block -+ final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // get index, when valid, return nswe -+ final int index = block.getIndexBelow(geoX, geoY, worldZ); -+ return index == -1 ? 0 : block.getNswe(index); -+ } -+ -+ /** -+ * Save region file to file. -+ * @param filename : The name of file to save. -+ * @return boolean : True when successful. -+ */ -+ private static boolean saveGeoBlocks(String filename) -+ { -+ try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) -+ { -+ // loop over region blocks and save each block -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[ix][iy].saveBlock(bos); -+ } -+ } -+ -+ // flush data to file -+ bos.flush(); -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ private static void loadGeoengineConfigs() -+ { -+ final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); -+ Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); -+ Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); -+ Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); -+ Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); -+ Config.PATHFINDING = geoData.getBoolean("PathFinding", true); -+ Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -+ Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); -+ Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); -+ Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); -+ Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); -+ Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); -+ } -+} -\ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/geodata/Readme.txt b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/geodata/Readme.txt index a480c8c380..2611882e56 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/geodata/Readme.txt +++ b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/geodata/Readme.txt @@ -23,8 +23,8 @@ a - Prerequisites b - Make it work ---------------------------------------------- -To make geodata working: -* unpack your geodata files into "/data/geodata" folder -* open "/config/GeoEngine.ini" with your favorite text editor and then edit following config: - - CoordSynchronize = 2 -* If you do not use any geodata files, the server will automatically change this setting to -1. +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. diff --git a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java index d5884ea7b8..d210def899 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java +++ b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java @@ -270,7 +270,7 @@ public class PrimevalIsle extends AbstractNpcAI final double cos = Math.cos(radian); final int newX = (int) (npc.getX() + (cos * distance)); final int newY = (int) (npc.getY() + (sin * distance)); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, npc.getZ(), npc.getInstanceId()); + final Location loc = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, npc.getZ(), npc.getInstanceId()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, loc, 0); } else if (ag_type == 1) diff --git a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/ai/others/FleeMonsters.java b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/ai/others/FleeMonsters.java index 906ef8e8b8..3bab5a3ad5 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/ai/others/FleeMonsters.java +++ b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/ai/others/FleeMonsters.java @@ -69,7 +69,7 @@ public class FleeMonsters extends AbstractNpcAI final int posX = (int) (npc.getX() + (FLEE_DISTANCE * Math.cos(radians))); final int posY = (int) (npc.getY() + (FLEE_DISTANCE * Math.sin(radians))); final int posZ = npc.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, attacker.getInstanceId()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, npc.getInstanceId()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); return super.onAttack(npc, attacker, damage, isSummon); } diff --git a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index 524652cbc1..18511c6541 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { @@ -133,8 +133,8 @@ public class AdminGeodata implements IAdminCommandHandler } case "admin_geomap": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y + " (" + ((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + "," + ((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + " to " + ((((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + "," + ((((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + ")"); break; } diff --git a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index a29a86b4f4..ad6fcd2b7a 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -19,9 +19,9 @@ package handlers.admincommandhandlers; import java.util.List; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; +import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.util.BuilderUtil; @@ -44,13 +44,13 @@ public class AdminPathNode implements IAdminCommandHandler } if (activeChar.getTarget() != null) { - final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceId()); + final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceId()); if (path == null) { BuilderUtil.sendSysMessage(activeChar, "No Route!"); return true; } - for (AbstractNodeLoc a : path) + for (Location a : path) { BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } diff --git a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/effecthandlers/Blink.java b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/effecthandlers/Blink.java index d2d82c3bd0..25c96e50ff 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/effecthandlers/Blink.java +++ b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/effecthandlers/Blink.java @@ -68,7 +68,7 @@ public class Blink extends AbstractEffect final int x = effected.getX() + x1; final int y = effected.getY() + y1; final int z = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceId()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceId()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, destination, FlyType.DUMMY)); effected.abortAttack(); diff --git a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/effecthandlers/EnemyCharge.java b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/effecthandlers/EnemyCharge.java index e0d12253d3..53041adad7 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/effecthandlers/EnemyCharge.java +++ b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/effecthandlers/EnemyCharge.java @@ -90,7 +90,7 @@ public class EnemyCharge extends AbstractEffect final int x = curX + (int) ((distance - offset) * cos); final int y = curY + (int) ((distance - offset) * sin); final int z = info.getEffected().getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(info.getEffector().getX(), info.getEffector().getY(), info.getEffector().getZ(), x, y, z, info.getEffector().getInstanceId()); + final Location destination = GeoEngine.getInstance().getValidLocation(info.getEffector().getX(), info.getEffector().getY(), info.getEffector().getZ(), x, y, z, info.getEffector().getInstanceId()); info.getEffector().broadcastPacket(new FlyToLocation(info.getEffector(), destination, FlyType.CHARGE)); // maybe is need force set X,Y,Z diff --git a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/effecthandlers/Fishing.java b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/effecthandlers/Fishing.java index 3b9a284fbd..c7da046bc7 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/effecthandlers/Fishing.java +++ b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/effecthandlers/Fishing.java @@ -236,7 +236,7 @@ public class Fishing extends AbstractEffect // always use water zone, fishing zone high z is high in the air... final int baitZ = waterZone.getWaterZ(); - if (!GeoEngine.getInstance().canSeeTarget(player, new Location(baitX, baitY, baitZ))) + if (!GeoEngine.getInstance().canSeeLocation(player, new Location(baitX, baitY, baitZ))) { return Integer.MIN_VALUE; } diff --git a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java index 3e313dda32..71fcae3db6 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java +++ b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java @@ -82,7 +82,7 @@ public class TeleportToTarget extends AbstractEffect final int x = (int) (px + (25 * Math.cos(ph))); final int y = (int) (py + (25 * Math.sin(ph))); final int z = target.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceId()); + final Location loc = GeoEngine.getInstance().getValidLocation(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceId()); creature.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); creature.broadcastPacket(new FlyToLocation(creature, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); creature.abortAttack(); diff --git a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/effecthandlers/ThrowUp.java b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/effecthandlers/ThrowUp.java index a6d06f9496..f1616cbeed 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/effecthandlers/ThrowUp.java +++ b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/effecthandlers/ThrowUp.java @@ -93,7 +93,7 @@ public class ThrowUp extends AbstractEffect final int x = info.getEffector().getX() - (int) (offset * cos); final int y = info.getEffector().getY() - (int) (offset * sin); final int z = info.getEffected().getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(info.getEffected().getX(), info.getEffected().getY(), info.getEffected().getZ(), x, y, z, info.getEffected().getInstanceId()); + final Location destination = GeoEngine.getInstance().getValidLocation(info.getEffected().getX(), info.getEffected().getY(), info.getEffected().getZ(), x, y, z, info.getEffected().getInstanceId()); info.getEffected().broadcastPacket(new FlyToLocation(info.getEffected(), destination, FlyType.THROW_UP)); // TODO: Review. info.getEffected().setXYZ(destination); diff --git a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java index 35cf9c3121..ddea5da753 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java +++ b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java @@ -65,7 +65,7 @@ public class RollingDice implements IItemHandler final int x = player.getX() + x1; final int y = player.getY() + y1; final int z = player.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceId()); + final Location destination = GeoEngine.getInstance().getValidLocation(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceId()); Broadcast.toSelfAndKnownPlayers(player, new Dice(player.getObjectId(), itemId, number, destination.getX(), destination.getY(), destination.getZ())); final SystemMessage sm = new SystemMessage(SystemMessageId.C1_HAS_ROLLED_A_S2); diff --git a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/instances/CrystalCaverns/CrystalCaverns.java b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/instances/CrystalCaverns/CrystalCaverns.java index 2dd1b5b562..c11c6db437 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/instances/CrystalCaverns/CrystalCaverns.java +++ b/L2J_Mobius_CT_2.4_Epilogue/dist/game/data/scripts/instances/CrystalCaverns/CrystalCaverns.java @@ -677,7 +677,7 @@ public class CrystalCaverns extends AbstractInstance final int _x = effector.getX() - (int) (offset * cos); final int _y = effector.getY() - (int) (offset * sin); final int _z = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), _x, _y, _z, effected.getInstanceId()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), _x, _y, _z, effected.getInstanceId()); effected.broadcastPacket(new FlyToLocation(effected, destination, FlyType.THROW_UP)); // maybe is need force set X,Y,Z diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/Config.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/Config.java index 9f8c953e75..c5458f04ee 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/Config.java @@ -59,6 +59,7 @@ import org.l2jmobius.commons.enums.ServerMode; import org.l2jmobius.commons.util.IXmlReader; import org.l2jmobius.commons.util.PropertiesParser; import org.l2jmobius.gameserver.enums.ChatType; +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; @@ -1060,12 +1061,17 @@ public class Config // GeoEngine // -------------------------------------------------- public static Path GEODATA_PATH; + public static GeoType GEODATA_TYPE; public static boolean PATHFINDING; public static String PATHFIND_BUFFERS; - public static float LOW_WEIGHT; - public static float MEDIUM_WEIGHT; - public static float HIGH_WEIGHT; - public static float DIAGONAL_WEIGHT; + public static int MOVE_WEIGHT; + public static int MOVE_WEIGHT_DIAG; + public static int OBSTACLE_WEIGHT; + 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; // -------------------------------------------------- // Custom Settings @@ -2585,12 +2591,17 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); + GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); - MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); - HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); - DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); + MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); + OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); + HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); + MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); + MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); // Load AllowedPlayerRaces config file (if exists) final PropertiesParser AllowedPlayerRaces = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE); diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/commons/util/CommonUtil.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/commons/util/CommonUtil.java index 6f26027539..7aafc74eac 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/commons/util/CommonUtil.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/commons/util/CommonUtil.java @@ -266,4 +266,15 @@ public class CommonUtil } return false; } + + /** + * @param numToTest : The number to test. + * @param min : The minimum limit. + * @param max : The maximum limit. + * @return the number or one of the limit (mininum / maximum). + */ + public static int limit(int numToTest, int min, int max) + { + return (numToTest > max) ? max : ((numToTest < min) ? min : numToTest); + } } diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/commons/util/Point2D.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/commons/util/Point2D.java new file mode 100644 index 0000000000..ebb6272e5e --- /dev/null +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/commons/util/Point2D.java @@ -0,0 +1,180 @@ +/* + * 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 . + */ +package org.l2jmobius.commons.util; + +import java.util.Objects; + +/** + * A datatype used to retain a 2D (x/y) point. It got the capability to be set and cleaned. + */ +public class Point2D +{ + protected volatile int _x; + protected volatile int _y; + + public Point2D(int x, int y) + { + _x = x; + _y = y; + } + + @Override + public Point2D clone() + { + return new Point2D(_x, _y); + } + + @Override + public String toString() + { + return _x + ", " + _y; + } + + @Override + public int hashCode() + { + return Objects.hash(_x, _y); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final Point2D other = (Point2D) obj; + return (_x == other._x) && (_y == other._y); + } + + /** + * @param x : The X coord to test. + * @param y : The Y coord to test. + * @return True if all coordinates equals this {@link Point2D} coordinates. + */ + public boolean equals(int x, int y) + { + return (_x == x) && (_y == y); + } + + public int getX() + { + return _x; + } + + public void setX(int x) + { + _x = x; + } + + public int getY() + { + return _y; + } + + public void setY(int y) + { + _y = y; + } + + public void set(int x, int y) + { + _x = x; + _y = y; + } + + /** + * Refresh the current {@link Point2D} using a reference {@link Point2D} and a distance. The new destination is calculated to go in opposite side of the {@link Point2D} reference.
+ *
+ * This method is perfect to calculate fleeing characters position. + * @param referenceLoc : The Point2D used as position. + * @param distance : The distance to be set between current and new position. + */ + public void setFleeing(Point2D referenceLoc, int distance) + { + final double xDiff = referenceLoc.getX() - _x; + final double yDiff = referenceLoc.getY() - _y; + + final double yxRation = Math.abs(xDiff / yDiff); + + final int y = (int) (distance / (yxRation + 1)); + final int x = (int) (y * yxRation); + + _x += (xDiff < 0 ? x : -x); + _y += (yDiff < 0 ? y : -y); + } + + public void clean() + { + _x = 0; + _y = 0; + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @return The distance between this {@Point2D} and some given coordinates. + */ + public double distance2D(int x, int y) + { + final double dx = (double) _x - x; + final double dy = (double) _y - y; + + return Math.sqrt((dx * dx) + (dy * dy)); + } + + /** + * @param point : The {@link Point2D} to test. + * @return The distance between this {@Point2D} and the {@link Point2D} set as parameter. + */ + public double distance2D(Point2D point) + { + return distance2D(point.getX(), point.getY()); + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of some given coordinates. + */ + public boolean isIn2DRadius(int x, int y, int radius) + { + return distance2D(x, y) < radius; + } + + /** + * @param point : The Point2D to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of the {@link Point2D} set as parameter. + */ + public boolean isIn2DRadius(Point2D point, int radius) + { + return distance2D(point) < radius; + } +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/AttackableAI.java index 765bcef311..dc0b2925b5 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/AttackableAI.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/AttackableAI.java @@ -710,7 +710,7 @@ public class AttackableAI extends CreatureAI } // Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast) - final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceId()); + final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceId()); moveTo(moveLoc.getX(), moveLoc.getY(), moveLoc.getZ()); } } @@ -941,7 +941,7 @@ public class AttackableAI extends CreatureAI final int newZ = npc.getZ() + 30; // Mobius: Verify destination. Prevents wall collision issues and fixes monsters not avoiding obstacles. - moveTo(GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceId())); + moveTo(GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceId())); } return; } diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/CreatureAI.java index 60ef097bfc..5bb0ea4d3a 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/CreatureAI.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/CreatureAI.java @@ -925,7 +925,7 @@ public class CreatureAI extends AbstractAI } // If pathfinding enabled the creature will go to the destination or it will go to the nearest obstacle. - setIntention(AI_INTENTION_MOVE_TO, Config.PATHFINDING ? GeoEngine.getInstance().canMoveToTargetLoc(_actor.getX(), _actor.getY(), _actor.getZ(), posX, posY, posZ, _actor.getInstanceId()) : new Location(posX, posY, posZ)); + setIntention(AI_INTENTION_MOVE_TO, Config.PATHFINDING ? GeoEngine.getInstance().getValidLocation(_actor.getX(), _actor.getY(), _actor.getZ(), posX, posY, posZ, _actor.getInstanceId()) : new Location(posX, posY, posZ)); } protected boolean maybeMoveToPosition(ILocational worldPosition, int offset) diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/SummonAI.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/SummonAI.java index d56be32b4c..217d288f9d 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/SummonAI.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/ai/SummonAI.java @@ -26,7 +26,6 @@ import org.l2jmobius.Config; import org.l2jmobius.commons.concurrent.ThreadPool; import org.l2jmobius.commons.util.Rnd; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; import org.l2jmobius.gameserver.model.WorldObject; import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.actor.Summon; @@ -51,7 +50,7 @@ public class SummonAI extends PlayableAI implements Runnable @Override protected void onIntentionAttack(Creature target) { - if (Config.PATHFINDING && (GeoEnginePathfinding.getInstance().findPath(_actor.getX(), _actor.getY(), _actor.getZ(), target.getX(), target.getY(), target.getZ(), _actor.getInstanceId()) == null)) + if (Config.PATHFINDING && (GeoEngine.getInstance().findPath(_actor.getX(), _actor.getY(), _actor.getZ(), target.getX(), target.getY(), target.getZ(), _actor.getInstanceId()) == null)) { return; } diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/data/SpawnTable.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/data/SpawnTable.java index 49bcc79768..60b1133986 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/data/SpawnTable.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/data/SpawnTable.java @@ -400,8 +400,8 @@ public class SpawnTable implements IXmlReader } // XML file for spawn - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); // Write info to XML @@ -476,8 +476,8 @@ public class SpawnTable implements IXmlReader if (update) { - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final int npcSpawnTemplateId = spawn.getNpcSpawnTemplateId(); final File spawnFile = npcSpawnTemplateId > 0 ? new File(_spawnTemplates.get(npcSpawnTemplateId)) : new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); final File tempFile = new File(spawnFile.getAbsolutePath().substring(Config.DATAPACK_ROOT.getAbsolutePath().length() + 1).replace('\\', '/') + ".tmp"); diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/enums/GeoType.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/enums/GeoType.java new file mode 100644 index 0000000000..f77aa5eac4 --- /dev/null +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/enums/GeoType.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.enums; + +public enum GeoType +{ + L2J("%d_%d.l2j"), + L2OFF("%d_%d_conv.dat"); + + private final String _filename; + + private GeoType(String filename) + { + _filename = filename; + } + + public String getFilename() + { + return _filename; + } +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java new file mode 100644 index 0000000000..e62f50a8df --- /dev/null +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java @@ -0,0 +1,144 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 7b814dd4fd..4679a89eb9 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,71 +16,62 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.channels.FileChannel.MapMode; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.logging.Level; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; import java.util.logging.Logger; import org.l2jmobius.Config; +import org.l2jmobius.commons.util.CommonUtil; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; -import org.l2jmobius.gameserver.geoengine.geodata.IRegion; -import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; -import org.l2jmobius.gameserver.geoengine.geodata.Region; +import org.l2jmobius.gameserver.enums.GeoType; +import org.l2jmobius.gameserver.enums.MoveDirectionType; +import org.l2jmobius.gameserver.geoengine.geodata.ABlock; +import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; +import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; +import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; +import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; -import org.l2jmobius.gameserver.model.interfaces.ILocational; -import org.l2jmobius.gameserver.util.GeoUtils; -import org.l2jmobius.gameserver.util.LinePointIterator; -import org.l2jmobius.gameserver.util.LinePointIterator3D; +import org.l2jmobius.gameserver.model.actor.Creature; -/** - * @author -Nemesiss-, HorridoJoho - */ public class GeoEngine { - private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - - private static final int WORLD_MIN_X = -655360; - private static final int WORLD_MIN_Y = -589824; - private static final int WORLD_MIN_Z = -16384; - - /** 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; - - /** The regions array */ - private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; - private static final int ELEVATED_SEE_OVER_DISTANCE = 2; - private static final int MAX_SEE_OVER_HEIGHT = 48; - protected GeoEngine() + private final ABlock[][] _blocks; + private final BlockNull _nullBlock; + + // Pre-allocated buffers. + private final BufferHolder[] _buffers; + + public GeoEngine() { LOGGER.info("GeoEngine: Initializing..."); - for (int i = 0; i < _regions.length(); i++) - { - _regions.set(i, NullRegion.INSTANCE); - } + // Initialize block container. + _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; + + // Load null block. + _nullBlock = new BlockNull(); + + // Initialize multilayer temporarily buffer. + BlockMultilayer.initialize(); + + // Load geo files according to geoengine config setup. int loaded = 0; try { @@ -91,23 +82,52 @@ public class GeoEngine final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); if (Files.exists(geoFilePath)) { - try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + // Region file is load-able, try to load it. + if (loadGeoBlocks(regionX, regionY)) { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); loaded++; } } + else + { + // Region file is not load-able, load null blocks. + loadNullBlocks(regionX, regionY); + } } } } catch (Exception e) { - LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + LOGGER.warning("GeoEngine: Failed to load geodata! " + e); System.exit(1); } - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + // Release multilayer block temporarily buffer. + BlockMultilayer.release(); + + String[] array = Config.PATHFIND_BUFFERS.split(";"); + _buffers = new BufferHolder[array.length]; + + int count = 0; + for (int i = 0; i < array.length; i++) + { + String buf = array[i]; + String[] args = buf.split("x"); + + try + { + int size = Integer.parseInt(args[1]); + count += size; + _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); + } + catch (Exception e) + { + LOGGER.warning("Could not load buffer setting:" + buf + ". " + e); + } + } + LOGGER.info("Loaded " + count + " node buffers."); + // Avoid wrong configs when no files are loaded. if ((loaded == 0) && Config.PATHFINDING) { @@ -117,616 +137,913 @@ public class GeoEngine } /** - * @param geoX - * @param geoY - * @return the region + * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer and log this situation. + * @param size : pre-calculated minimal required size + * @return NodeBuffer : buffer */ - private IRegion getRegion(int geoX, int geoY) + private NodeBuffer getBuffer(int size) { - final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); - if ((region < 0) || (region >= _regions.length())) + NodeBuffer current = null; + for (BufferHolder holder : _buffers) { - return null; - } - return _regions.get(region); - } - - /** - * @param filePath - * @param regionX - * @param regionY - * @throws IOException - */ - 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()).load().order(ByteOrder.LITTLE_ENDIAN))); - } - } - - /** - * @param regionX - * @param regionY - */ - public void unloadRegion(int regionX, int regionY) - { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); - } - - /** - * @param geoX - * @param geoY - * @return if geodata exist - */ - public boolean hasGeoPos(int geoX, int geoY) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return false; - } - return region.hasGeo(); - } - - /** - * Checks the specified position for available geodata. - * @param x the world x - * @param y the world y - * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise - */ - public boolean hasGeo(int x, int y) - { - return hasGeoPos(getGeoX(x), getGeoY(y)); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe check - */ - public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return true; - } - return region.checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe anti-corner cut - */ - public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) - { - boolean can = true; - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - return can && checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the nearest Z value - */ - public int getNearestZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNearestZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next lower Z value - */ - public int getNextLowerZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextLowerZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next higher Z value - */ - public int getNextHigherZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextHigherZ(geoX, geoY, worldZ); - } - - /** - * Gets the Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHeight(int x, int y, int z) - { - return getNearestZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next lower Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getLowerHeight(int x, int y, int z) - { - return getNextLowerZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next higher Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHigherHeight(int x, int y, int z) - { - return getNextHigherZ(getGeoX(x), getGeoY(y), z); - } - - /** - * @param worldX - * @return the geo X - */ - public int getGeoX(int worldX) - { - return (worldX - WORLD_MIN_X) / 16; - } - - /** - * @param worldY - * @return the geo Y - */ - public int getGeoY(int worldY) - { - return (worldY - WORLD_MIN_Y) / 16; - } - - /** - * @param worldZ - * @return the geo Z - */ - public int getGeoZ(int worldZ) - { - return (worldZ - WORLD_MIN_Z) / 16; - } - - /** - * @param geoX - * @return the world X - */ - public int getWorldX(int geoX) - { - return (geoX * 16) + WORLD_MIN_X + 8; - } - - /** - * @param geoY - * @return the world Y - */ - public int getWorldY(int geoY) - { - return (geoY * 16) + WORLD_MIN_Y + 8; - } - - /** - * @param geoZ - * @return the world Z - */ - public int getWorldZ(int geoZ) - { - return (geoZ * 16) + WORLD_MIN_Z + 8; - } - - /** - * Can see target. Doors as target always return true. Checks doors between. - * @param cha the character - * @param target the target - * @return {@code true} if the character can see the target (LOS), {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, WorldObject target) - { - if (target.isDoor()) - { - // Can always see doors. - return true; - } - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), target.getX(), target.getY(), target.getZ(), target.getInstanceId()); - } - - /** - * Can see target. Checks doors between. - * @param cha the character - * @param worldPosition the world position - * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) - { - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instanceId - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @param tInstanceId the target's instanceId - * @return - */ - public boolean canSeeTarget(int x, int y, int z, int instanceId, int tx, int ty, int tz, int tInstanceId) - { - return (instanceId != tInstanceId) ? false : canSeeTarget(x, y, z, instanceId, tx, ty, tz); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instanceId - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int x, int y, int z, int instanceId, int tx, int ty, int tz) - { - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instanceId, true)) - { - return false; - } - return canSeeTarget(x, y, z, tx, ty, tz); - } - - /** - * @param prevX - * @param prevY - * @param prevGeoZ - * @param curX - * @param curY - * @param nswe - * @return the LOS Z value - */ - private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) - { - if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) - { - throw new RuntimeException("Multiple directions!"); - } - - if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) - { - return getNearestZ(curX, curY, prevGeoZ); - } - - return getNextHigherZ(curX, curY, prevGeoZ); - } - - /** - * Can see target. Does not check doors between. - * @param xValue the x coordinate - * @param yValue the y coordinate - * @param zValue the z coordinate - * @param txValue the target's x coordinate - * @param tyValue the target's y coordinate - * @param tzValue the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) - { - int x = xValue; - int y = yValue; - int tx = txValue; - int ty = tyValue; - - int geoX = getGeoX(x); - int geoY = getGeoY(y); - int tGeoX = getGeoX(tx); - int tGeoY = getGeoY(ty); - - int z = getNearestZ(geoX, geoY, zValue); - int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - // Fastpath. - if ((geoX == tGeoX) && (geoY == tGeoY)) - { - if (hasGeoPos(tGeoX, tGeoY)) - { - return z == tz; - } - return true; - } - - if (tz > z) - { - int tmp = tx; - tx = x; - x = tmp; - - tmp = ty; - ty = y; - y = tmp; - - tmp = tz; - tz = z; - z = tmp; - - tmp = tGeoX; - tGeoX = geoX; - geoX = tmp; - - tmp = tGeoY; - tGeoY = geoY; - geoY = tmp; - } - - final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); - // First point is guaranteed to be available, skip it, we can always see our own position. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - final int prevZ = pointIter.z(); - int prevGeoZ = prevZ; - int ptIndex = 0; - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - - if ((curX == prevX) && (curY == prevY)) + // Find proper size of buffer. + if (holder._size < size) { continue; } - final int beeCurZ = pointIter.z(); - int curGeoZ = prevGeoZ; - - // Check if the position has geodata. - if (hasGeoPos(curX, curY)) + // Find unlocked NodeBuffer. + for (NodeBuffer buffer : holder._buffer) { - final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); - curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); - int maxHeight; - if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) + if (!buffer.isLocked()) { - maxHeight = z + MAX_SEE_OVER_HEIGHT; - } - else - { - maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; + continue; } - boolean canSeeThrough = false; - if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) + return buffer; + } + + LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); + + // NodeBuffer not found, allocate temporary buffer. + current = new NodeBuffer(holder._size); + current.isLocked(); + } + + return current; + } + + /** + * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + * @return boolean : True, when geodata file was loaded without problem. + */ + private boolean loadGeoBlocks(int regionX, int regionY) + { + final String filename = String.format(Config.GEODATA_TYPE.getFilename(), regionX, regionY); + final String filepath = Config.GEODATA_PATH + "\\" + filename; + + // Standard load. + try (RandomAccessFile raf = new RandomAccessFile(filepath, "r"); + FileChannel fc = raf.getChannel()) + { + // Initialize file buffer. + MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // Load 18B header for L2OFF geodata (1st and 2nd byte...region X and Y). + if (Config.GEODATA_TYPE == GeoType.L2OFF) + { + for (int i = 0; i < 18; i++) { - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) + buffer.get(); + } + } + + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Loop over region blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + if (Config.GEODATA_TYPE == GeoType.L2J) { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) - { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); + // Get block type. + final byte type = buffer.get(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + case GeoStructure.TYPE_MULTILAYER_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + default: + { + throw new IllegalArgumentException("Unknown block type: " + type); + } + } } else { - canSeeThrough = true; + // Get block type. + final short type = buffer.getShort(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + default: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + } } } + } + + // Check data consistency. + if (buffer.remaining() > 0) + { + LOGGER.warning("Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); + } + + // Loading was successful. + return true; + } + catch (Exception e) + { + // An error occured while loading, load null blocks. + LOGGER.warning("Error loading " + filename + " region file. " + e); + + // Replace whole region file with null blocks. + loadNullBlocks(regionX, regionY); + + // Loading was not successful. + return false; + } + } + + /** + * Loads null blocks. Used when no region file is detected or an error occurs during loading. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + */ + private void loadNullBlocks(int regionX, int regionY) + { + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Load all null blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + _blocks[blockX + ix][blockY + iy] = _nullBlock; + } + } + } + + /** + * Converts world X to geodata X. + * @param worldX + * @return int : Geo X + */ + public static int getGeoX(int worldX) + { + return (worldX - World.WORLD_X_MIN) >> 4; + } + + /** + * Converts world Y to geodata Y. + * @param worldY + * @return int : Geo Y + */ + public static int getGeoY(int worldY) + { + return (worldY - World.WORLD_Y_MIN) >> 4; + } + + /** + * Converts geodata X to world X. + * @param geoX + * @return int : World X + */ + public static int getWorldX(int geoX) + { + return (geoX << 4) + World.WORLD_X_MIN + 8; + } + + /** + * Converts geodata Y to world Y. + * @param geoY + * @return int : World Y + */ + public static int getWorldY(int geoY) + { + return (geoY << 4) + World.WORLD_Y_MIN + 8; + } + + /** + * Returns block of geodata on given coordinates. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return {@link ABlock} : Block of geodata. + */ + public ABlock getBlock(int geoX, int geoY) + { + final int x = geoX / GeoStructure.BLOCK_CELLS_X; + if ((x < 0) || (x >= GeoStructure.GEO_BLOCKS_X)) + { + return null; + } + final int y = geoY / GeoStructure.BLOCK_CELLS_Y; + if ((y < 0) || (y >= GeoStructure.GEO_BLOCKS_Y)) + { + return null; + } + return _blocks[x][y]; + } + + /** + * Check if geo coordinates has geo. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return boolean : True, if given geo coordinates have geodata + */ + public boolean hasGeoPos(int geoX, int geoY) + { + final ABlock block = getBlock(geoX, geoY); + return (block != null) && block.hasGeoPos(); + } + + /** + * Returns the height of cell, which is closest to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell geodata Z coordinate, closest to given coordinates. + */ + public short getHeightNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return (short) worldZ; + } + return block.getHeightNearest(geoX, geoY, worldZ); + } + + /** + * Returns the NSWE flag byte of cell, which is closes to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. + */ + public byte getNsweNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return GeoStructure.CELL_FLAG_ALL; + } + return block.getNsweNearest(geoX, geoY, worldZ); + } + + /** + * Check if world coordinates has geo. + * @param worldX : World X + * @param worldY : World Y + * @return boolean : True, if given world coordinates have geodata + */ + public boolean hasGeo(int worldX, int worldY) + { + return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param loc : The location used as reference. + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(Location loc) + { + return getHeightNearest(getGeoX(loc.getX()), getGeoY(loc.getY()), loc.getZ()); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param worldX : world x + * @param worldY : world y + * @param worldZ : world z + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(int worldX, int worldY, int worldZ) + { + return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); + } + + /** + * Check line of sight from {@link WorldObject} to {@link WorldObject}.
+ * @param object : The origin object. + * @param target : The target object. + * @return True, when object can see target. + */ + public boolean canSeeTarget(WorldObject object, WorldObject target) + { + // Can always see doors. + if (target.isDoor()) + { + return true; + } + + if (object.getInstanceId() != target.getInstanceId()) + { + return false; + } + + if (DoorData.getInstance().checkIfDoorsBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceId(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceId())) + { + return false; + } + + // Get object's and target's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getTemplate().getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + double theight = 0; + if (target instanceof Creature) + { + theight += (((Creature) target).getTemplate().getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + return canSee(object.getX(), object.getY(), object.getZ(), oheight, target.getX(), target.getY(), target.getZ(), theight) && canSee(target.getX(), target.getY(), target.getZ(), theight, object.getX(), object.getY(), object.getZ(), oheight); + } + + /** + * Check line of sight from {@link WorldObject} to {@link Location}.
+ * Note: The check uses {@link Location}'s real Z coordinate (e.g. point above ground), not its geodata representation. + * @param object : The origin object. + * @param position : The target position. + * @return True, when object can see position. + */ + public boolean canSeeLocation(WorldObject object, Location position) + { + // Get object and location coordinates. + int ox = object.getX(); + int oy = object.getY(); + int oz = object.getZ(); + int tx = position.getX(); + int ty = position.getY(); + int tz = position.getZ(); + + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, object.getInstanceId(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, object.getInstanceId())) + { + return false; + } + + // Get object's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getTemplate().getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + // Perform geodata check. + return canSee(ox, oy, oz, oheight, tx, ty, tz, 0) && canSee(tx, ty, tz, 0, ox, oy, oz, oheight); + } + + /** + * Simple check for origin to target visibility.
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param oheight : The height of origin, used as start point. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param theight : The height of target, used as end point. + * @return True, when origin can see target. + */ + public boolean canSee(int ox, int oy, int oz, double oheight, int tx, int ty, int tz, double theight) + { + // Get origin geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get target geodata coordinates. + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check being on same cell and layer (index). + // Note: Get index must use origin height increased by cell height, the method returns index to height exclusive self. + int index = block.getIndexBelow(gox, goy, oz + GeoStructure.CELL_HEIGHT); + if (index < 0) + { + return false; + } + + if ((gox == gtx) && (goy == gty)) + { + return index == block.getIndexBelow(gtx, gty, tz + GeoStructure.CELL_HEIGHT); + } + + // Get ground and nswe flag. + int groundZ = block.getHeight(index); + int nswe = block.getNswe(index); + + // Get delta coordinates, slope of line (XY, XZ) and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double dz = (tz + theight) - (oz + oheight); + final double m = (double) dy / dx; + final double mz = dz / Math.sqrt((dx * dx) + (dy * dy)); + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); + + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) + { + // Set next cell in X direction. + gridX += mdt.getStepX(); + gox += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); - if (!canSeeThrough) - { - return false; - } + // Set next cell in Y direction. + gridY += mdt.getStepY(); + goy += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevGeoZ = curGeoZ; - ++ptIndex; + // Get block of the next cell. + block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get line of sight height (including Z slope). + double losz = oz + oheight + Config.MAX_OBSTACLE_HEIGHT; + losz += mz * Math.sqrt(((checkX - ox) * (checkX - ox)) + ((checkY - oy) * (checkY - oy))); + + // Check line of sight going though wall (vertical check). + + // Get index of particular layer, based on last iterated cell conditions. + boolean canMove = (nswe & dir) != 0; + if (canMove) + { + // No wall present, get next cell below current cell. + index = block.getIndexBelow(gox, goy, groundZ + GeoStructure.CELL_IGNORE_HEIGHT); + } + else + { + // Wall present, get next cell above current cell. + index = block.getIndexAbove(gox, goy, groundZ - (2 * GeoStructure.CELL_HEIGHT)); + } + + // Next cell's does not exist (no geodata with valid condition), return fail. + if (index < 0) + { + return false; + } + + // Get next cell's layer height. + int z = block.getHeight(index); + + // Perform sine of sight check (next cell is above line of sight line), return fail. + if (z > losz) + { + return false; + } + + // Next cell is accessible, update z and NSWE. + groundZ = z; + nswe = block.getNswe(index); } + // Iteration is completed, no obstacle is found. return true; } /** - * Move check. - * @param x the x coordinate - * @param y the y coordinate - * @param zValue the z coordinate - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tzValue the target's z coordinate - * @param instanceId the instance id - * @return the last Location (x,y,z) where player can walk - just before wall + * Check movement from coordinates to coordinates.
+ * Note: The Z coordinates are supposed to be already validated geodata coordinates. + * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instanceId + * @return True, when target coordinates are reachable from origin coordinates. */ - public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, int instanceId) + public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) { - final int geoX = getGeoX(x); - final int geoY = getGeoY(y); - final int z = getNearestZ(geoX, geoY, zValue); - final int tGeoX = getGeoX(tx); - final int tGeoY = getGeoY(ty); - final int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instanceId, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instanceId, false)) { - return new Location(x, y, getHeight(x, y, z)); - } - if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instanceId)) - { - return new Location(x, y, getHeight(x, y, z)); + return false; } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = z; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instanceId)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return false; + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + int goz = getHeightNearest(gox, goy, oz); + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check movement within same cell. + if ((gox == gtx) && (goy == gty)) + { + return goz == getHeight(tx, ty, tz); + } + + // Get nswe flag. + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - // Can't move, return previous location. - return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check point heading into obstacle, if so return current point. + if ((nswe & dir) == 0) + { + return false; + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return false; + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != tz)) - { - // Different floors, return start location. - return new Location(x, y, z); - } - - return new Location(tx, ty, tz); + // When origin Z is target Z, the move is successful. + return goz == getHeight(tx, ty, tz); } /** - * Checks if its possible to move from one location to another. - * @param fromX the X coordinate to start checking from - * @param fromY the Y coordinate to start checking from - * @param fromZvalue the Z coordinate to start checking from - * @param toX the X coordinate to end checking at - * @param toY the Y coordinate to end checking at - * @param toZvalue the Z coordinate to end checking at - * @param instanceId the instance - * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + * Check movement from origin to target coordinates. Returns last available point in the checked path.
+ * Target X and Y reachable and Z is on same floor: + *
    + *
  • Location of the target with corrected Z value from geodata.
  • + *
+ * Target X and Y reachable but Z is on another floor: + *
    + *
  • Location of the origin with corrected Z value from geodata.
  • + *
+ * Target X and Y not reachable: + *
    + *
  • Last accessible location in destination to target.
  • + *
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instanceId + * @return The {@link Location} representing last point of movement (e.g. just before wall). */ - public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, int instanceId) + public Location getValidLocation(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) { - final int geoX = getGeoX(fromX); - final int geoY = getGeoY(fromY); - final int fromZ = getNearestZ(geoX, geoY, fromZvalue); - final int tGeoX = getGeoX(toX); - final int tGeoY = getGeoY(toY); - final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); - - if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instanceId, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instanceId, false)) { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instanceId)) - { - return false; + return new Location(ox, oy, oz); } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = fromZ; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instanceId)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return new Location(ox, oy, oz); + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + final int gtz = getHeightNearest(gtx, gty, tz); + int goz = getHeightNearest(gox, goy, oz); + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - return false; - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check target cell is out of geodata grid (world coordinates). + if ((nx < 0) || (nx >= GeoStructure.GEO_CELLS_X) || (ny < 0) || (ny >= GeoStructure.GEO_CELLS_Y)) + { + return new Location(checkX, checkY, goz); + } + + // Check point heading into obstacle, if so return current (border) point. + if ((nswe & dir) == 0) + { + return new Location(checkX, checkY, goz); + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current (border) point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return new Location(checkX, checkY, goz); + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) - { - // Different floors. - return false; - } - - return true; + // Compare Z coordinates: + // If same, path is okay, return target point and fix its Z geodata coordinate. + // If not same, path is does not exist, return origin point. + return goz == gtz ? new Location(tx, ty, gtz) : new Location(ox, oy, oz); } + /** + * Returns the list of location objects as a result of complete path calculation. + * @param ox : origin x + * @param oy : origin y + * @param oz : origin z + * @param tx : target x + * @param ty : target y + * @param tz : target z + * @param instanceId + * @return {@code List} : complete path from nodes + */ + public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) + { + // Get origin and check existing geo coords. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + if (!hasGeoPos(gox, goy)) + { + return Collections.emptyList(); + } + + int goz = getHeightNearest(gox, goy, oz); + + // Get target and check existing geo coords. + int gtx = getGeoX(tx); + int gty = getGeoY(ty); + if (!hasGeoPos(gtx, gty)) + { + return Collections.emptyList(); + } + + int gtz = getHeightNearest(gtx, gty, tz); + + // Prepare buffer for pathfinding calculations. + NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + if (buffer == null) + { + return Collections.emptyList(); + } + + // Find path. + List path = null; + try + { + path = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + if (path.isEmpty()) + { + return Collections.emptyList(); + } + } + catch (Exception e) + { + return Collections.emptyList(); + } + finally + { + buffer.free(); + } + + // Check path. + if (path.size() < 3) + { + return path; + } + + // Get path list iterator. + ListIterator point = path.listIterator(); + + // Get node A (origin). + int nodeAx = ox; + int nodeAy = oy; + int nodeAz = goz; + + // Get node B. + Location nodeB = point.next(); + + // Iterate thought the path to optimize it. + while (point.hasNext()) + { + // Get node C. + Location nodeC = path.get(point.nextIndex()); + + // Check movement from node A to node C. + if (canMoveToTarget(nodeAx, nodeAy, nodeAz, nodeC.getX(), nodeC.getY(), nodeC.getZ(), instanceId)) + { + // Can move from node A to node C. + // Remove node B. + point.remove(); + } + else + { + // Can not move from node A to node C. + // Set node A (node B is part of path, update A coordinates). + nodeAx = nodeB.getX(); + nodeAy = nodeB.getY(); + nodeAz = nodeB.getZ(); + } + + // Set node B. + nodeB = point.next(); + } + + return path; + } + + /** + * NodeBuffer container with specified size and count of separate buffers. + */ + private static class BufferHolder + { + final int _size; + final List _buffer; + + public BufferHolder(int size, int count) + { + _size = size; + _buffer = new ArrayList<>(count); + + for (int i = 0; i < count; i++) + { + _buffer.add(new NodeBuffer(size)); + } + } + } + + /** + * Returns the instance of the {@link GeoEngine}. + * @return {@link GeoEngine} : The instance. + */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -736,4 +1053,4 @@ public class GeoEngine { protected static final GeoEngine INSTANCE = new GeoEngine(); } -} +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java deleted file mode 100644 index 43ac4d89b8..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ /dev/null @@ -1,300 +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 . - */ -package org.l2jmobius.gameserver.geoengine; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; -import org.l2jmobius.gameserver.model.World; - -/** - * @author -Nemesiss- - */ -public class GeoEnginePathfinding -{ - private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); - - private BufferInfo[] _buffers; - - protected GeoEnginePathfinding() - { - try - { - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - - _buffers = 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); - } - - _buffers[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"); - } - } - - public boolean pathNodesExist(short regionoffset) - { - return false; - } - - public List findPath(int x, int y, int z, int tx, int ty, int tz, int instanceId) - { - 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)))); - if (buffer == null) - { - return null; - } - - List path = null; - try - { - final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); - - if (result == null) - { - return null; - } - - path = constructPath(result); - } - catch (Exception e) - { - LOGGER.warning(e.getMessage()); - return null; - } - finally - { - buffer.free(); - } - - // check path - if (path.size() < 3) - { - return path; - } - - int currentX, currentY, currentZ; - ListIterator middlePoint; - - middlePoint = path.listIterator(); - currentX = x; - currentY = y; - currentZ = z; - - while (middlePoint.hasNext()) - { - final AbstractNodeLoc locMiddle = middlePoint.next(); - if (!middlePoint.hasNext()) - { - break; - } - - final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); - if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instanceId)) - { - middlePoint.remove(); - } - else - { - currentX = locMiddle.getX(); - currentY = locMiddle.getY(); - currentZ = locMiddle.getZ(); - } - } - - return path; - } - - /** - * Convert geodata position to pathnode position - * @param geo_pos - * @return pathnode position - */ - public short getNodePos(int geo_pos) - { - return (short) (geo_pos >> 3); // OK? - } - - /** - * Convert node position to pathnode block position - * @param node_pos - * @return pathnode block position (0...255) - */ - public short getNodeBlock(int node_pos) - { - return (short) (node_pos % 256); - } - - public byte getRegionX(int node_pos) - { - return (byte) ((node_pos >> 8) + World.TILE_X_MIN); - } - - public byte getRegionY(int node_pos) - { - return (byte) ((node_pos >> 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 node_x rx - * @return - */ - public int calculateWorldX(short node_x) - { - return World.MAP_MIN_X + (node_x * 128) + 48; - } - - /** - * Convert pathnode y to World y position - * @param node_y - * @return - */ - public int calculateWorldY(short node_y) - { - return World.MAP_MIN_Y + (node_y * 128) + 48; - } - - private List constructPath(AbstractNode nodeValue) - { - final LinkedList path = new LinkedList<>(); - int previousDirectionX = Integer.MIN_VALUE; - int previousDirectionY = Integer.MIN_VALUE; - int directionX, directionY; - - AbstractNode node = nodeValue; - while (node.getParent() != null) - { - directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); - directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); - - // only add a new route point if moving direction changes - if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) - { - previousDirectionX = directionX; - previousDirectionY = directionY; - - path.addFirst(node.getLoc()); - node.setLoc(null); - } - - node = node.getParent(); - } - - return path; - } - - private CellNodeBuffer alloc(int size) - { - CellNodeBuffer current = null; - for (BufferInfo i : _buffers) - { - if (i.mapSize >= size) - { - for (CellNodeBuffer buf : i.buffer) - { - if (buf.lock()) - { - current = buf; - break; - } - } - if (current != null) - { - break; - } - - // not found, allocate temporary buffer - current = new CellNodeBuffer(i.mapSize); - current.lock(); - if (i.buffer.size() < i.count) - { - i.buffer.add(current); - break; - } - } - } - - return current; - } - - private static final class BufferInfo - { - final int mapSize; - final int count; - ArrayList buffer; - - public BufferInfo(int size, int cnt) - { - mapSize = size; - count = cnt; - buffer = new ArrayList<>(count); - } - } - - public static GeoEnginePathfinding getInstance() - { - return SingletonHolder.INSTANCE; - } - - private static class SingletonHolder - { - protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); - } -} diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java new file mode 100644 index 0000000000..49ec35aa1c --- /dev/null +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java @@ -0,0 +1,85 @@ +/* + * 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 . + */ +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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @param index : Index of the cell. + * @return short : Cell geodata Z coordinate, below given coordinates. + */ + public abstract byte getNswe(int index); +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java new file mode 100644 index 0000000000..f5997bf287 --- /dev/null +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java @@ -0,0 +1,130 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java new file mode 100644 index 0000000000..9af9d2a28a --- /dev/null +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java @@ -0,0 +1,95 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java new file mode 100644 index 0000000000..31fbf5d477 --- /dev/null +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java new file mode 100644 index 0000000000..f77d21d84b --- /dev/null +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java @@ -0,0 +1,68 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +public class BlockNull extends ABlock +{ + @Override + public boolean hasGeoPos() + { + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java deleted file mode 100644 index 61ea4fcf82..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java +++ /dev/null @@ -1,48 +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 . - */ -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() - { - } -} diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java deleted file mode 100644 index 3c8de1a7f7..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java +++ /dev/null @@ -1,77 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java deleted file mode 100644 index 1ccdefe3da..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java +++ /dev/null @@ -1,56 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java new file mode 100644 index 0000000000..691b164e0c --- /dev/null +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ +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_ALL = 0x0F; + + // 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; +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java deleted file mode 100644 index 25eafc5a04..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java +++ /dev/null @@ -1,42 +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 . - */ -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); -} diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java deleted file mode 100644 index 0d2c765002..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java +++ /dev/null @@ -1,47 +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 . - */ -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(); -} diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java deleted file mode 100644 index 52df457360..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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); - - // 1 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; - } -} diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java deleted file mode 100644 index 72a5a635c7..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java +++ /dev/null @@ -1,55 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author HorridoJoho - */ -public final class NullRegion implements IRegion -{ - public static final NullRegion INSTANCE = new NullRegion(); - - @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 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; - } -} diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java deleted file mode 100644 index fc924cb875..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java +++ /dev/null @@ -1,92 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java deleted file mode 100644 index 5087513ed9..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java +++ /dev/null @@ -1,87 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public abstract class AbstractNode -{ - private T _loc; - private AbstractNode _parent; - - public AbstractNode(T loc) - { - _loc = loc; - } - - public void setParent(AbstractNode p) - { - _parent = p; - } - - public AbstractNode getParent() - { - return _parent; - } - - public T getLoc() - { - return _loc; - } - - public void setLoc(T l) - { - _loc = l; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); - return result; - } - - @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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java deleted file mode 100644 index 7223b5b2be..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java +++ /dev/null @@ -1,33 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -/** - * @author -Nemesiss- - */ -public abstract class AbstractNodeLoc -{ - public abstract int getX(); - - public abstract int getY(); - - public abstract int getZ(); - - public abstract int getNodeX(); - - public abstract int getNodeY(); -} diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java deleted file mode 100644 index 3425234e0e..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public class CellNode extends AbstractNode -{ - 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java deleted file mode 100644 index 522610bb7c..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java +++ /dev/null @@ -1,348 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -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 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) - { - _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(); - } - - public final List debugPath() - { - final List 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 void getNeighbors() - { - if (!_current.getLoc().canGoAll()) - { - 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); - } - - // SouthEast - if ((nodeE != null) && (nodeS != null)) - { - if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) - { - addNode(x + 1, y + 1, z, true); - } - } - - // SouthWest - if ((nodeS != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) - { - addNode(x - 1, y + 1, z, true); - } - } - - // NorthEast - if ((nodeN != null) && (nodeE != null)) - { - if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) - { - addNode(x + 1, y - 1, z, true); - } - } - - // NorthWest - if ((nodeN != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) - { - addNode(x - 1, y - 1, z, true); - } - } - } - - private 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(); - // reinit node if needed - if (result.getLoc() != null) - { - result.getLoc().set(x, y, z); - } - else - { - result.setLoc(new NodeLoc(x, y, z)); - } - } - - return result; - } - - private 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 boolean isHighWeight(int x, int y, int z) - { - final CellNode result = getNode(x, y, z); - if (result == null) - { - return true; - } - - if (!result.getLoc().canGoAll()) - { - return true; - } - if (Math.abs(result.getLoc().getZ() - z) > 16) - { - return true; - } - - return false; - } - - private 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java new file mode 100644 index 0000000000..fa5ca43c2f --- /dev/null +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -0,0 +1,111 @@ +/* + * 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 . + */ +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 geodata values. + private int _geoX; + private int _geoY; + private byte _nswe; + + // 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; + + _costG = 0; + _costH = 0; + _costF = 0; + + _parent = null; + } + + public void setGeo(int gx, int gy, int gz, byte nswe) + { + super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); + + _geoX = gx; + _geoY = gy; + _nswe = nswe; + } + + 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 int getCostF() + { + return _costF; + } + + public Node getParent() + { + return _parent; + } + + @Override + public int compareTo(Node o) + { + return _costF - o._costF; + } +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java new file mode 100644 index 0000000000..b464212c96 --- /dev/null +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -0,0 +1,368 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.awt.Color; +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; +import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; + +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 _opened; + // Container holding Nodes already explored. + private final List _closed; + + // Target coordinates. + private int _gtx; + private int _gty; + private int _gtz; + + // Pathfinding statistics. + private long _timeStamp; + private long _lastElapsedTime; + + 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 findPath(int gox, int goy, int goz, int gtx, int gty, int gtz) + { + // Set start timestamp. + _timeStamp = System.currentTimeMillis(); + + // 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)); + _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 constructPath() + { + // Create result. + final LinkedList 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; + } + + /** + * Creates list of Nodes to show debug path. + * @param debug : The debug packet to add debug informations in. + */ + public void debugPath(ExServerPrimitive debug) + { + // Add all opened node as yellow points. + for (Node n : _opened) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); + } + + // Add all opened node as blue points. + for (Node n : _closed) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); + } + } + + 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; + + _lastElapsedTime = System.currentTimeMillis() - _timeStamp; + _lock.unlock(); + } + + public long getElapsedTime() + { + return _lastElapsedTime; + } + + /** + * Expand the current {@link Node} by exploring its neighbors (axially and diagonally). + */ + private void expand() + { + // Movement is blocked, skip. + final byte nswe = _current.getNSWE(); + if (nswe == 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; + + // Can move north, expand. + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + } + + // Can move south, expand. + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + } + + // Can move west, expand. + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move east, expand. + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move north-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move north-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + } + + /** + * 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 weight : The weight of movement to the new node. + * @return The nswe of the added node. Blank, if not added. + */ + private byte addNode(int gx, int gy, int gzValue, int weight) + { + // 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]; + + // Set node geodata coordinates. + node.setGeo(gx, gy, gz, nswe); + + // 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, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + _opened.add(node); + _bufferIndex++; + return nswe; + } + + /** + * Calculate cost H value, calculated using diagonal distance method.
+ * 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. + final int dx = Math.abs(gx - _gtx); + final int dy = Math.abs(gy - _gty); + final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + + // Get diagonal and axial differences to the target. + final int dd = Math.min(dx, dy); + final int da = Math.max(dx, dy) - dd; + + // Calculate the diagonal distance of the node to the target. + return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + } +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java deleted file mode 100644 index e3bad04b25..0000000000 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; - -/** - * @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 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 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; - // return super.hashCode(); - } - - @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; - if (_x != other._x) - { - return false; - } - if (_y != other._y) - { - return false; - } - if (_goNorth != other._goNorth) - { - return false; - } - if (_goEast != other._goEast) - { - return false; - } - if (_goSouth != other._goSouth) - { - return false; - } - if (_goWest != other._goWest) - { - return false; - } - if (_geoHeight != other._geoHeight) - { - return false; - } - return true; - } -} diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java index 28a267c40b..50c3a499ca 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java @@ -91,15 +91,15 @@ public class ZoneManager implements IXmlReader private static final Map SETTINGS = new HashMap<>(); private static final int SHIFT_BY = 15; - private static final int OFFSET_X = Math.abs(World.MAP_MIN_X >> SHIFT_BY); - private static final int OFFSET_Y = Math.abs(World.MAP_MIN_Y >> SHIFT_BY); + private static final int OFFSET_X = Math.abs(World.WORLD_X_MIN >> SHIFT_BY); + private static final int OFFSET_Y = Math.abs(World.WORLD_Y_MIN >> SHIFT_BY); private final Map, ConcurrentHashMap> _classZones = new ConcurrentHashMap<>(); private final Map _spawnTerritories = new ConcurrentHashMap<>(); private final AtomicInteger _lastDynamicId = new AtomicInteger(300000); private List _debugItems; - private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.MAP_MAX_X >> SHIFT_BY) + OFFSET_X + 1][(World.MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y + 1]; + private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.WORLD_X_MAX >> SHIFT_BY) + OFFSET_X + 1][(World.WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y + 1]; /** * Instantiates a new zone manager. @@ -178,7 +178,6 @@ public class ZoneManager implements IXmlReader String zoneType; String zoneShape; final List rs = new ArrayList<>(); - for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling()) { if ("list".equalsIgnoreCase(n.getNodeName())) @@ -195,7 +194,6 @@ public class ZoneManager implements IXmlReader if ("zone".equalsIgnoreCase(d.getNodeName())) { attrs = d.getAttributes(); - attribute = attrs.getNamedItem("type"); if (attribute != null) { @@ -244,7 +242,6 @@ public class ZoneManager implements IXmlReader minZ = parseInteger(attrs, "minZ"); maxZ = parseInteger(attrs, "maxZ"); - zoneType = parseString(attrs, "type"); zoneShape = parseString(attrs, "shape"); @@ -366,7 +363,6 @@ public class ZoneManager implements IXmlReader attrs = cd.getAttributes(); final String name = attrs.getNamedItem("name").getNodeValue(); final String val = attrs.getNamedItem("val").getNodeValue(); - temp.setParameter(name, val); } else if ("spawn".equalsIgnoreCase(cd.getNodeName()) && (temp instanceof ZoneRespawn)) @@ -383,7 +379,6 @@ public class ZoneManager implements IXmlReader attrs = cd.getAttributes(); final String race = attrs.getNamedItem("name").getNodeValue(); final String point = attrs.getNamedItem("point").getNodeValue(); - ((RespawnZone) temp).addRaceRespawnPoint(race, point); } } @@ -410,7 +405,6 @@ public class ZoneManager implements IXmlReader final int bx = ((x + 1) - OFFSET_X) << SHIFT_BY; final int ay = (y - OFFSET_Y) << SHIFT_BY; final int by = ((y + 1) - OFFSET_Y) << SHIFT_BY; - if (temp.getZone().intersectsRectangle(ax, bx, ay, by)) { _zoneRegions[x][y].getZones().put(temp.getId(), temp); @@ -463,7 +457,7 @@ public class ZoneManager implements IXmlReader LOGGER.info(getClass().getSimpleName() + ": Loaded " + _classZones.size() + " zone classes and " + getSize() + " zones."); LOGGER.info(getClass().getSimpleName() + ": Loaded " + _spawnTerritories.size() + " NPC spawn territoriers."); final OptionalInt maxId = _classZones.values().stream().flatMap(map -> map.keySet().stream()).mapToInt(Integer.class::cast).filter(value -> value < 300000).max(); - LOGGER.info(getClass().getSimpleName() + ": Last static id: " + maxId.getAsInt()); + LOGGER.info(getClass().getSimpleName() + ": Last static id " + maxId.getAsInt() + "."); } /** diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/Location.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/Location.java index 480e95582d..c413725eef 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/Location.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/Location.java @@ -16,30 +16,35 @@ */ package org.l2jmobius.gameserver.model; +import java.util.Objects; + +import org.l2jmobius.commons.util.Point2D; import org.l2jmobius.gameserver.model.interfaces.ILocational; import org.l2jmobius.gameserver.model.interfaces.IPositionable; /** - * Location data transfer object.
- * Contains coordinates data, heading and instance Id. - * @author Zoey76 + * A datatype used to retain a 3D (x/y/z/heading/instanceId) point. It got the capability to be set and cleaned. */ -public class Location implements IPositionable +public class Location extends Point2D implements IPositionable { - protected int _x; - protected int _y; - protected int _z; - private int _heading; - private int _instanceId; + protected volatile int _z; + protected volatile int _heading; + protected volatile int _instanceId; public Location(int x, int y, int z) { - this(x, y, z, 0, 0); + super(x, y); + _z = z; + _heading = 0; + _instanceId = 0; } public Location(int x, int y, int z, int heading) { - this(x, y, z, heading, 0); + super(x, y); + _z = z; + _heading = heading; + _instanceId = 0; } public Location(WorldObject obj) @@ -49,13 +54,20 @@ public class Location implements IPositionable public Location(int x, int y, int z, int heading, int instanceId) { - _x = x; - _y = y; + super(x, y); _z = z; _heading = heading; _instanceId = instanceId; } + public Location(StatSet set) + { + super(set.getInt("x", 0), set.getInt("y", 0)); + _z = set.getInt("z", 0); + _heading = set.getInt("heading", 0); + _instanceId = set.getInt("instanceId", 0); + } + /** * Get the x coordinate. * @return the x coordinate @@ -166,15 +178,35 @@ public class Location implements IPositionable _instanceId = loc.getInstanceId(); } + @Override + public void clean() + { + super.clean(); + _z = 0; + _instanceId = 0; + } + + @Override + public Location clone() + { + return new Location(_x, _y, _z); + } + + @Override + public int hashCode() + { + return (31 * super.hashCode()) + Objects.hash(_z); + } + @Override public boolean equals(Object obj) { - if (!(obj instanceof Location)) + if (obj instanceof Location) { - return false; + final Location loc = (Location) obj; + return (getX() == loc.getX()) && (getY() == loc.getY()) && (getZ() == loc.getZ()) && (getHeading() == loc.getHeading()) && (getInstanceId() == loc.getInstanceId()); } - final Location loc = (Location) obj; - return (getX() == loc.getX()) && (getY() == loc.getY()) && (getZ() == loc.getZ()) && (getHeading() == loc.getHeading()) && (getInstanceId() == loc.getInstanceId()); + return false; } @Override diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/World.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/World.java index fcecd966b1..0e0a7f5793 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/World.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/World.java @@ -65,19 +65,19 @@ public class World public static final int TILE_Y_MAX = 26; public static final int TILE_ZERO_COORD_X = 20; public static final int TILE_ZERO_COORD_Y = 18; - public static final int MAP_MIN_X = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; - public static final int MAP_MIN_Y = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; + public static final int WORLD_X_MIN = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; + public static final int WORLD_Y_MIN = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; - public static final int MAP_MAX_X = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; - public static final int MAP_MAX_Y = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; + public static final int WORLD_X_MAX = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; + public static final int WORLD_Y_MAX = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; /** Calculated offset used so top left region is 0,0 */ - public static final int OFFSET_X = Math.abs(MAP_MIN_X >> SHIFT_BY); - public static final int OFFSET_Y = Math.abs(MAP_MIN_Y >> SHIFT_BY); + public static final int OFFSET_X = Math.abs(WORLD_X_MIN >> SHIFT_BY); + public static final int OFFSET_Y = Math.abs(WORLD_Y_MIN >> SHIFT_BY); /** Number of regions. */ - private static final int REGIONS_X = (MAP_MAX_X >> SHIFT_BY) + OFFSET_X; - private static final int REGIONS_Y = (MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y; + private static final int REGIONS_X = (WORLD_X_MAX >> SHIFT_BY) + OFFSET_X; + private static final int REGIONS_Y = (WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y; /** Map containing all the players in game. */ private static final Map _allPlayers = new ConcurrentHashMap<>(); @@ -783,9 +783,6 @@ public class World } } - /** - * @return the current instance of World - */ public static World getInstance() { return SingletonHolder.INSTANCE; diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/WorldObject.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/WorldObject.java index f85f231ede..a86fa382d6 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/WorldObject.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/WorldObject.java @@ -191,23 +191,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif synchronized (this) { int spawnX = x; - if (spawnX > World.MAP_MAX_X) + if (spawnX > World.WORLD_X_MAX) { - spawnX = World.MAP_MAX_X - 5000; + spawnX = World.WORLD_X_MAX - 5000; } - if (spawnX < World.MAP_MIN_X) + if (spawnX < World.WORLD_X_MIN) { - spawnX = World.MAP_MIN_X + 5000; + spawnX = World.WORLD_X_MIN + 5000; } int spawnY = y; - if (spawnY > World.MAP_MAX_Y) + if (spawnY > World.WORLD_Y_MAX) { - spawnY = World.MAP_MAX_Y - 5000; + spawnY = World.WORLD_Y_MAX - 5000; } - if (spawnY < World.MAP_MIN_Y) + if (spawnY < World.WORLD_Y_MIN) { - spawnY = World.MAP_MIN_Y + 5000; + spawnY = World.WORLD_Y_MIN + 5000; } // Set the x,y,z position of the WorldObject. If flagged with _isSpawned, setXYZ will automatically update world region, so avoid that. @@ -529,23 +529,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif public void setXYZInvisible(int x, int y, int z) { int correctX = x; - if (correctX > World.MAP_MAX_X) + if (correctX > World.WORLD_X_MAX) { - correctX = World.MAP_MAX_X - 5000; + correctX = World.WORLD_X_MAX - 5000; } - if (correctX < World.MAP_MIN_X) + if (correctX < World.WORLD_X_MIN) { - correctX = World.MAP_MIN_X + 5000; + correctX = World.WORLD_X_MIN + 5000; } int correctY = y; - if (correctY > World.MAP_MAX_Y) + if (correctY > World.WORLD_Y_MAX) { - correctY = World.MAP_MAX_Y - 5000; + correctY = World.WORLD_Y_MAX - 5000; } - if (correctY < World.MAP_MIN_Y) + if (correctY < World.WORLD_Y_MIN) { - correctY = World.MAP_MIN_Y + 5000; + correctY = World.WORLD_Y_MIN + 5000; } setXYZ(correctX, correctY, z); diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/Creature.java index 9fad6a87c6..6bac4f76e3 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -57,8 +57,6 @@ import org.l2jmobius.gameserver.enums.ShotType; import org.l2jmobius.gameserver.enums.Team; import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.InstanceManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; @@ -3378,7 +3376,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -4338,8 +4336,8 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe final int originalX = x; final int originalY = y; final int originalZ = z; - final int gtx = (originalX - World.MAP_MIN_X) >> 4; - final int gty = (originalY - World.MAP_MIN_Y) >> 4; + final int gtx = (originalX - World.WORLD_X_MIN) >> 4; + final int gty = (originalY - World.WORLD_Y_MIN) >> 4; if (isOnGeodataPath()) { try @@ -4362,7 +4360,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe && !(((curZ - z) > 300) && (distance < 300))) // Prohibit correcting destination if character wants to fall. { // location different if destination wasn't reached (or just z coord is different) - final Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(curX, curY, curZ, x, y, z, getInstanceId()); + final Location destiny = GeoEngine.getInstance().getValidLocation(curX, curY, curZ, x, y, z, getInstanceId()); x = destiny.getX(); y = destiny.getY(); z = destiny.getZ(); @@ -4376,7 +4374,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isAfraid() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId()); + m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { if (isPlayer() && !_isFlying && !isInWater) diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/Summon.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/Summon.java index f9ff199335..8fed320f50 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/Summon.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/Summon.java @@ -29,7 +29,6 @@ import org.l2jmobius.gameserver.enums.Race; import org.l2jmobius.gameserver.enums.ShotType; import org.l2jmobius.gameserver.enums.Team; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; import org.l2jmobius.gameserver.handler.IItemHandler; import org.l2jmobius.gameserver.handler.ItemHandler; import org.l2jmobius.gameserver.instancemanager.TerritoryWarManager; @@ -107,7 +106,7 @@ public abstract class Summon extends Playable final int x = owner.getX(); final int y = owner.getY(); final int z = owner.getZ(); - final Location location = GeoEngine.getInstance().canMoveToTargetLoc(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, owner.getInstanceId()); + final Location location = GeoEngine.getInstance().getValidLocation(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, owner.getInstanceId()); setXYZInvisible(location.getX(), location.getY(), location.getZ()); } @@ -646,7 +645,7 @@ public abstract class Summon extends Playable return false; } - if ((this != target) && skill.isPhysical() && Config.PATHFINDING && (GeoEnginePathfinding.getInstance().findPath(getX(), getY(), getZ(), target.getX(), target.getY(), target.getZ(), getInstanceId()) == null)) + if ((this != target) && skill.isPhysical() && Config.PATHFINDING && (GeoEngine.getInstance().findPath(getX(), getY(), getZ(), target.getX(), target.getY(), target.getZ(), getInstanceId()) == null)) { sendPacket(SystemMessageId.CANNOT_SEE_TARGET); return false; diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index 27da7474e8..c5b0ac9f46 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -8948,7 +8948,7 @@ public class PlayerInstance extends Playable { if (sklTargetType == TargetType.GROUND) { - if (!GeoEngine.getInstance().canSeeTarget(this, _currentSkillWorldPosition)) + if (!GeoEngine.getInstance().canSeeLocation(this, _currentSkillWorldPosition)) { sendPacket(SystemMessageId.CANNOT_SEE_TARGET); sendPacket(ActionFailed.STATIC_PACKET); diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java index 5504283200..6c4f8a0e68 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java @@ -1521,7 +1521,7 @@ public class ItemInstance extends WorldObject if (dropper != null) { - final Location dropDest = GeoEngine.getInstance().canMoveToTargetLoc(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, dropper.getInstanceId()); + final Location dropDest = GeoEngine.getInstance().getValidLocation(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, dropper.getInstanceId()); x = dropDest.getX(); y = dropDest.getY(); z = dropDest.getZ(); diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java index 3e9c72f851..709397b56d 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java @@ -114,7 +114,7 @@ public class ValidatePosition implements IClientIncomingPacket { if (player.isFalling(_z)) { - final int nearestZ = GeoEngine.getInstance().getHigherHeight(_x, _y, _z); + final int nearestZ = GeoEngine.getInstance().getHeight(_x, _y, _z); if (player.getZ() < nearestZ) { player.setXYZ(_x, _y, nearestZ); diff --git a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/util/GeoUtils.java index 4600a0fc53..19a979819e 100644 --- a/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_CT_2.4_Epilogue/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -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.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; @@ -30,21 +30,21 @@ public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getInstance().getWorldX(iter.x()); - final int wy = GeoEngine.getInstance().getWorldY(iter.y()); + final int wx = GeoEngine.getWorldX(iter.x()); + final int wy = GeoEngine.getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public final class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); - int wy = GeoEngine.getInstance().getWorldY(prevY); + int wx = GeoEngine.getWorldX(prevX); + int wy = GeoEngine.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.getInstance().getWorldX(curX); - wy = GeoEngine.getInstance().getWorldY(curY); + wx = GeoEngine.getWorldX(curX); + wy = GeoEngine.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().checkNearestNswe(x, y, z, nswe)) + if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0) { return Color.GREEN; } @@ -109,9 +109,8 @@ public final class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final GeoEngine ge = GeoEngine.getInstance(); - final int playerGx = ge.getGeoX(player.getX()); - final int playerGy = ge.getGeoY(player.getY()); + final int playerGx = GeoEngine.getGeoX(player.getX()); + final int playerGy = GeoEngine.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -135,32 +134,32 @@ public final class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = ge.getWorldX(gx); - final int y = ge.getWorldY(gy); - final int z = ge.getNearestZ(gx, gy, player.getZ()); + final int x = GeoEngine.getWorldX(gx); + final int y = GeoEngine.getWorldY(gy); + final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); + Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); 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, Cell.NSWE_EAST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); 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, Cell.NSWE_SOUTH); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); 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, Cell.NSWE_WEST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); 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); @@ -188,41 +187,41 @@ public final class GeoUtils { if (y > lastY) { - return Cell.NSWE_SOUTH_EAST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E; } else if (y < lastY) { - return Cell.NSWE_NORTH_EAST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E; } else { - return Cell.NSWE_EAST; + return GeoStructure.CELL_FLAG_E; } } else if (x < lastX) // west { if (y > lastY) { - return Cell.NSWE_SOUTH_WEST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W; } else if (y < lastY) { - return Cell.NSWE_NORTH_WEST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W; } else { - return Cell.NSWE_WEST; + return GeoStructure.CELL_FLAG_W; } } else // unchanged x { if (y > lastY) { - return Cell.NSWE_SOUTH; + return GeoStructure.CELL_FLAG_S; } else if (y < lastY) { - return Cell.NSWE_NORTH; + return GeoStructure.CELL_FLAG_N; } else { diff --git a/L2J_Mobius_CT_2.6_HighFive/dist/game/config/GeoEngine.ini b/L2J_Mobius_CT_2.6_HighFive/dist/game/config/GeoEngine.ini index 609e8a89a7..e740c678dd 100644 --- a/L2J_Mobius_CT_2.6_HighFive/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_CT_2.6_HighFive/dist/game/config/GeoEngine.ini @@ -6,33 +6,44 @@ # at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ 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 + # ================================================================= -# Path finding +# Pathfinding # ================================================================= # 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 +# an alternative path (e.g. walk around obstacle), default: True +PathFinding = True -# Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 -PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 +# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 -# Weight for nodes without obstacles far from walls -LowWeight = 0.5 +# Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 +MoveWeight = 10 +MoveWeightDiag = 14 -# Weight for nodes near walls -MediumWeight = 2 +# 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 -# Weight for nodes with obstacles -HighWeight = 3 +# 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. -# Default: LowWeight * sqrt(2) -DiagonalWeight = 0.707 +# Maximum number of generated nodes per one path-finding process, default 3500 +MaxIterations = 3500 # ================================================================= -# Other +# Line of Sight # ================================================================= -# Correct player Z after falling through the ground. -CorrectPlayerZ = False +# 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 diff --git a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/geodata/L2D_GeoEngine.patch b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/geodata/L2D_GeoEngine.patch deleted file mode 100644 index b11c767ce4..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/geodata/L2D_GeoEngine.patch +++ /dev/null @@ -1,6075 +0,0 @@ -Index: dist/game/GeoDataConverter.bat -=================================================================== ---- dist/game/GeoDataConverter.bat (nonexistent) -+++ dist/game/GeoDataConverter.bat (working copy) -@@ -0,0 +1,6 @@ -+@echo off -+title L2D geodata converter -+ -+java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter -+ -+pause -Index: dist/game/GeoDataConverter.sh -=================================================================== ---- dist/game/GeoDataConverter.sh (nonexistent) -+++ dist/game/GeoDataConverter.sh (working copy) -@@ -0,0 +1,4 @@ -+#! /bin/sh -+ -+java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 -+ -Index: dist/game/config/GeoEngine.ini -=================================================================== ---- dist/game/config/GeoEngine.ini (revision 8319) -+++ dist/game/config/GeoEngine.ini (working copy) -@@ -1,6 +1,10 @@ - # ================================================================= - # Geodata - # ================================================================= -+# Because of real-time performance we are using geodata files only in -+# diagonal L2D format now (using filename e.g. 22_16.l2d). -+# L2D geodata can be obtained by conversion of existing L2J or L2OFF geodata. -+# Launch "GeoDataConverter.bat/sh" and follow instructions to start the conversion. - - # 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/ -@@ -13,6 +17,16 @@ - CoordSynchronize = 2 - - # ================================================================= -+# Path checking -+# ================================================================= -+ -+# 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 finding - # ================================================================= - -@@ -23,19 +37,23 @@ - # Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - --# Weight for nodes without obstacles far from walls --LowWeight = 0.5 -+# Base path weight, when moving from one node to another on axis direction, default: 10 -+BaseWeight = 10 - --# Weight for nodes near walls --MediumWeight = 2 -+# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -+DiagonalWeight = 14 - --# Weight for nodes with obstacles --HighWeight = 3 -+# When movement flags of target node is blocked to any direction, multiply movement weight by this multiplier. -+# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 10 -+ObstacleMultiplier = 10 - --# Weight for diagonal movement. --# Default: LowWeight * sqrt(2) --DiagonalWeight = 0.707 -+# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 20 -+# For proper function must be higher than BaseWeight and/or DiagonalWeight. -+HeuristicWeight = 20 - -+# Maximum number of generated nodes per one path-finding process, default 3500 -+MaxIterations = 3500 -+ - # ================================================================= - # Other - # ================================================================= -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (revision 8319) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (working copy) -@@ -55,9 +55,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -@@ -73,9 +72,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (revision 8319) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (working copy) -@@ -18,11 +18,11 @@ - - import java.util.List; - --import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -+import org.l2jmobius.gameserver.geoengine.GeoEngine; - import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -+import org.l2jmobius.gameserver.network.SystemMessageId; - import org.l2jmobius.gameserver.util.BuilderUtil; - - public class AdminPathNode implements IAdminCommandHandler -@@ -37,29 +37,30 @@ - { - if (command.equals("admin_path_find")) - { -- if (!Config.PATHFINDING) -- { -- BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); -- return true; -- } - if (activeChar.getTarget() != null) - { -- final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceId()); -+ final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceId()); - if (path == null) - { -- BuilderUtil.sendSysMessage(activeChar, "No Route!"); -- return true; -+ BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); - } -- for (AbstractNodeLoc a : path) -+ else - { -- BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); -+ for (Location point : path) -+ { -+ BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); -+ } - } - } - else - { -- BuilderUtil.sendSysMessage(activeChar, "No Target!"); -+ activeChar.sendPacket(SystemMessageId.INVALID_TARGET); - } - } -+ else -+ { -+ return false; -+ } - return true; - } - -Index: java/org/l2jmobius/Config.java -=================================================================== ---- java/org/l2jmobius/Config.java (revision 8388) -+++ java/org/l2jmobius/Config.java (working copy) -@@ -32,7 +32,6 @@ - import java.net.UnknownHostException; - import java.nio.charset.StandardCharsets; - import java.nio.file.Files; --import java.nio.file.Path; - import java.nio.file.Paths; - import java.util.ArrayList; - import java.util.Arrays; -@@ -1059,14 +1058,17 @@ - // -------------------------------------------------- - // GeoEngine - // -------------------------------------------------- -- public static Path GEODATA_PATH; -+ public static String GEODATA_PATH; -+ public static int COORD_SYNCHRONIZE; -+ public static int PART_OF_CHARACTER_HEIGHT; -+ public static int MAX_OBSTACLE_HEIGHT; - public static boolean PATHFINDING; - public static String PATHFIND_BUFFERS; -- public static float LOW_WEIGHT; -- public static float MEDIUM_WEIGHT; -- public static float HIGH_WEIGHT; -- public static float DIAGONAL_WEIGHT; -- public static int COORD_SYNCHRONIZE; -+ public static int BASE_WEIGHT; -+ public static int DIAGONAL_WEIGHT; -+ public static int HEURISTIC_WEIGHT; -+ public static int OBSTACLE_MULTIPLIER; -+ public static int MAX_ITERATIONS; - public static boolean CORRECT_PLAYER_Z; - - // -------------------------------------------------- -@@ -2595,14 +2597,17 @@ - - // Load GeoEngine config file (if exists) - final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); -- GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); -+ GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); -+ COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); -+ MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); - PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -- LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); -- MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); -- HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); -- DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); -- COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); -+ DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); -+ OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); -+ HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); -+ MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); - CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); - - // Load AllowedPlayerRaces config file (if exists) -Index: java/org/l2jmobius/gameserver/ai/SummonAI.java -=================================================================== ---- java/org/l2jmobius/gameserver/ai/SummonAI.java (revision 8319) -+++ java/org/l2jmobius/gameserver/ai/SummonAI.java (working copy) -@@ -26,7 +26,6 @@ - import org.l2jmobius.commons.concurrent.ThreadPool; - import org.l2jmobius.commons.util.Rnd; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; - import org.l2jmobius.gameserver.model.WorldObject; - import org.l2jmobius.gameserver.model.actor.Creature; - import org.l2jmobius.gameserver.model.actor.Summon; -@@ -51,7 +50,7 @@ - @Override - protected void onIntentionAttack(Creature target) - { -- if (Config.PATHFINDING && (GeoEnginePathfinding.getInstance().findPath(_actor.getX(), _actor.getY(), _actor.getZ(), target.getX(), target.getY(), target.getZ(), _actor.getInstanceId()) == null)) -+ if (Config.PATHFINDING && (GeoEngine.getInstance().findPath(_actor.getX(), _actor.getY(), _actor.getZ(), target.getX(), target.getY(), target.getZ(), _actor.getInstanceId()) == null)) - { - return; - } -Index: java/org/l2jmobius/gameserver/geoengine/GeoEngine.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (revision 8352) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (working copy) -@@ -16,99 +16,89 @@ - */ - package org.l2jmobius.gameserver.geoengine; - --import java.io.IOException; -+import java.io.File; - import java.io.RandomAccessFile; - import java.nio.ByteOrder; --import java.nio.channels.FileChannel.MapMode; --import java.nio.file.Files; --import java.nio.file.Path; --import java.util.concurrent.atomic.AtomicReferenceArray; --import java.util.logging.Level; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.List; - import java.util.logging.Logger; - - import org.l2jmobius.Config; - import org.l2jmobius.gameserver.data.xml.DoorData; - import org.l2jmobius.gameserver.data.xml.FenceData; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; --import org.l2jmobius.gameserver.geoengine.geodata.IRegion; --import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; --import org.l2jmobius.gameserver.geoengine.geodata.Region; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.World; - import org.l2jmobius.gameserver.model.WorldObject; --import org.l2jmobius.gameserver.model.interfaces.ILocational; --import org.l2jmobius.gameserver.util.GeoUtils; --import org.l2jmobius.gameserver.util.LinePointIterator; --import org.l2jmobius.gameserver.util.LinePointIterator3D; -+import org.l2jmobius.gameserver.model.actor.Creature; -+import org.l2jmobius.gameserver.util.MathUtil; - - /** -- * @author -Nemesiss-, HorridoJoho -+ * @author Hasha - */ - public class GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); -+ protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - -- private static final int WORLD_MIN_X = -655360; -- private static final int WORLD_MIN_Y = -589824; -- private static final int WORLD_MIN_Z = -16384; -+ private final ABlock[][] _blocks; -+ private final BlockNull _nullBlock; - -- /** 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; -- -- /** The regions array */ -- private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); -- -- private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; -- private static final int ELEVATED_SEE_OVER_DISTANCE = 2; -- private static final int MAX_SEE_OVER_HEIGHT = 48; -- -- protected GeoEngine() -+ /** -+ * GeoEngine constructor. Loads all geodata files of chosen geodata format. -+ */ -+ public GeoEngine() - { - LOGGER.info("GeoEngine: Initializing..."); -- for (int i = 0; i < _regions.length(); i++) -- { -- _regions.set(i, NullRegion.INSTANCE); -- } - -+ // initialize block container -+ _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; -+ -+ // load null block -+ _nullBlock = new BlockNull(); -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files according to geoengine config setup - int loaded = 0; -- try -+ long fileSize = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { -- for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { -- for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) -+ final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); -+ if (f.exists() && !f.isDirectory()) - { -- final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); -- if (Files.exists(geoFilePath)) -+ // region file is load-able, try to load it -+ if (loadGeoBlocks(rx, ry)) - { -- try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) -- { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -- loaded++; -- } -+ loaded++; -+ fileSize += f.length(); - } - } -+ else -+ { -+ // region file is not load-able, load null blocks -+ loadNullBlocks(rx, ry); -+ } - } - } -- catch (Exception e) -+ -+ LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -+ if (loaded > 0) - { -- LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); -- System.exit(1); -+ LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); - } - -- LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -- -- // Avoid wrong configs when no files are loaded. -+ // avoid wrong configs when no files are loaded - if (loaded == 0) - { - if (Config.PATHFINDING) -@@ -122,619 +112,820 @@ - LOGGER.info("GeoEngine: Forcing CoordSynchronize setting to -1."); - } - } -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); - } - - /** -- * @param geoX -- * @param geoY -- * @return the region -+ * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. -+ * @return boolean : True, when geodata file was loaded without problem. - */ -- private IRegion getRegion(int geoX, int geoY) -+ private final boolean loadGeoBlocks(int regionX, int regionY) - { -- final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); -- if ((region < 0) || (region >= _regions.length())) -+ final String filename = String.format(GeoFormat.L2D.getFilename(), regionX, regionY); -+ -+ // standard load -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) - { -- return null; -+ // initialize file buffer -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, GeoFormat.L2D); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ } -+ -+ // check data consistency -+ if (buffer.remaining() > 0) -+ { -+ LOGGER.warning("GeoEngine: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ } -+ -+ // loading was successful -+ return true; - } -- return _regions.get(region); -- } -- -- /** -- * @param filePath -- * @param regionX -- * @param regionY -- * @throws IOException -- */ -- 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")) -+ catch (Exception e) - { -- _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -+ // an error occured while loading, load null blocks -+ LOGGER.warning("GeoEngine: Error while loading " + filename + " region file."); -+ LOGGER.warning(e.getMessage()); -+ -+ // replace whole region file with null blocks -+ loadNullBlocks(regionX, regionY); -+ -+ // loading was not successful -+ return false; - } - } - - /** -- * @param regionX -- * @param regionY -+ * Loads null blocks. Used when no region file is detected or an error occurs during loading. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. - */ -- public void unloadRegion(int regionX, int regionY) -+ private final void loadNullBlocks(int regionX, int regionY) - { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); -- } -- -- /** -- * @param geoX -- * @param geoY -- * @return if geodata exist -- */ -- public boolean hasGeoPos(int geoX, int geoY) -- { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // load all null blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { -- return false; -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[blockX + ix][blockY + iy] = _nullBlock; -+ } - } -- return region.hasGeo(); - } - -+ // GEODATA - GENERAL -+ - /** -- * Checks the specified position for available geodata. -- * @param x the world x -- * @param y the world y -- * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise -+ * Converts world X to geodata X. -+ * @param worldX -+ * @return int : Geo X - */ -- public boolean hasGeo(int x, int y) -+ public static int getGeoX(int worldX) - { -- return hasGeoPos(getGeoX(x), getGeoY(y)); -+ return (MathUtil.limit(worldX, World.MAP_MIN_X, World.MAP_MAX_X) - World.MAP_MIN_X) >> 4; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe check -+ * Converts world Y to geodata Y. -+ * @param worldY -+ * @return int : Geo Y - */ -- public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) -+ public static int getGeoY(int worldY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return true; -- } -- return region.checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(worldY, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.MAP_MIN_Y) >> 4; - } - - /** -+ * Converts geodata X to world X. - * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe anti-corner cut -+ * @return int : World X - */ -- public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) -+ public static int getWorldX(int geoX) - { -- boolean can = true; -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- return can && checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(geoX, 0, GeoStructure.GEO_CELLS_X) << 4) + World.MAP_MIN_X + 8; - } - - /** -- * @param geoX -+ * Converts geodata Y to world Y. - * @param geoY -- * @param worldZ -- * @return the nearest Z value -+ * @return int : World Y - */ -- public int getNearestZ(int geoX, int geoY, int worldZ) -+ public static int getWorldY(int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return worldZ; -- } -- return region.getNearestZ(geoX, geoY, worldZ); -+ return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next lower Z value -+ * Returns block of geodata on given coordinates. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return {@link ABlock} : Block of geodata. - */ -- public int getNextLowerZ(int geoX, int geoY, int worldZ) -+ private final ABlock getBlock(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final int x = geoX / GeoStructure.BLOCK_CELLS_X; -+ final int y = geoY / GeoStructure.BLOCK_CELLS_Y; -+ -+ // if x or y is out of array return null -+ if ((x > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) - { -- return worldZ; -+ return _blocks[x][y]; - } -- return region.getNextLowerZ(geoX, geoY, worldZ); -+ return null; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next higher Z value -+ * Check if geo coordinates has geo. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return boolean : True, if given geo coordinates have geodata - */ -- public int getNextHigherZ(int geoX, int geoY, int worldZ) -+ public boolean hasGeoPos(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final ABlock block = getBlock(geoX, geoY); -+ if (block == null) // null block check - { -- return worldZ; -+ // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) -+ // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); -+ return false; - } -- return region.getNextHigherZ(geoX, geoY, worldZ); -+ return block.hasGeoPos(); - } - - /** -- * Gets the Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the height of cell, which is closest to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, closest to given coordinates. - */ -- public int getHeight(int x, int y, int z) -+ public short getHeightNearest(int geoX, int geoY, int worldZ) - { -- return getNearestZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getHeightNearest(geoX, geoY, worldZ) : (short) worldZ; - } - - /** -- * Gets the next lower Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the NSWE flag byte of cell, which is closes to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. - */ -- public int getLowerHeight(int x, int y, int z) -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) - { -- return getNextLowerZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getNsweNearest(geoX, geoY, worldZ) : (byte) 0xFF; - } - - /** -- * Gets the next higher Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Check if world coordinates has geo. -+ * @param worldX : World X -+ * @param worldY : World Y -+ * @return boolean : True, if given world coordinates have geodata - */ -- public int getHigherHeight(int x, int y, int z) -+ public boolean hasGeo(int worldX, int worldY) - { -- return getNextHigherZ(getGeoX(x), getGeoY(y), z); -+ return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); - } - - /** -- * @param worldX -- * @return the geo X -+ * Returns closest Z coordinate according to geodata. -+ * @param worldX : world x -+ * @param worldY : world y -+ * @param worldZ : world z -+ * @return short : nearest Z coordinates according to geodata - */ -- public int getGeoX(int worldX) -+ public short getHeight(int worldX, int worldY, int worldZ) - { -- return (worldX - WORLD_MIN_X) / 16; -+ return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - -- /** -- * @param worldY -- * @return the geo Y -- */ -- public int getGeoY(int worldY) -- { -- return (worldY - WORLD_MIN_Y) / 16; -- } -+ // PATHFINDING - - /** -- * @param worldZ -- * @return the geo Z -+ * Check line of sight from {@link WorldObject} to {@link WorldObject}. -+ * @param origin : The origin object. -+ * @param target : The target object. -+ * @return {@code boolean} : True if origin can see target - */ -- public int getGeoZ(int worldZ) -+ public boolean canSeeTarget(WorldObject origin, WorldObject target) - { -- return (worldZ - WORLD_MIN_Z) / 16; -- } -- -- /** -- * @param geoX -- * @return the world X -- */ -- public int getWorldX(int geoX) -- { -- return (geoX * 16) + WORLD_MIN_X + 8; -- } -- -- /** -- * @param geoY -- * @return the world Y -- */ -- public int getWorldY(int geoY) -- { -- return (geoY * 16) + WORLD_MIN_Y + 8; -- } -- -- /** -- * @param geoZ -- * @return the world Z -- */ -- public int getWorldZ(int geoZ) -- { -- return (geoZ * 16) + WORLD_MIN_Z + 8; -- } -- -- /** -- * Can see target. Doors as target always return true. Checks doors between. -- * @param cha the character -- * @param target the target -- * @return {@code true} if the character can see the target (LOS), {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, WorldObject target) -- { -- if (target.isDoor()) -+ if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) - { -- // Can always see doors. - return true; - } -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), target.getX(), target.getY(), target.getZ(), target.getInstanceId()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param cha the character -- * @param worldPosition the world position -- * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) -- { -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instanceId -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @param tInstanceId the target's instanceId -- * @return -- */ -- public boolean canSeeTarget(int x, int y, int z, int instanceId, int tx, int ty, int tz, int tInstanceId) -- { -- return (instanceId != tInstanceId) ? false : canSeeTarget(x, y, z, instanceId, tx, ty, tz); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instanceId -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -- */ -- public boolean canSeeTarget(int x, int y, int z, int instanceId, int tx, int ty, int tz) -- { -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instanceId, true)) -+ -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = target.getX(); -+ final int ty = target.getY(); -+ final int tz = target.getZ(); -+ -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceId(), true)) - { - return false; - } -- return canSeeTarget(x, y, z, tx, ty, tz); -- } -- -- /** -- * @param prevX -- * @param prevY -- * @param prevGeoZ -- * @param curX -- * @param curY -- * @param nswe -- * @return the LOS Z value -- */ -- private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) -- { -- if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceId())) - { -- throw new RuntimeException("Multiple directions!"); -+ return false; - } - -- if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- return getNearestZ(curX, curY, prevGeoZ); -+ return true; - } - -- return getNextHigherZ(curX, curY, prevGeoZ); -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) -+ { -+ return true; -+ } -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) -+ { -+ return goz == gtz; -+ } -+ -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) -+ { -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight() * 2; -+ } -+ -+ double theight = 0; -+ if (target.isCreature()) -+ { -+ theight = ((Creature) target).getTemplate().getCollisionHeight() * 2; -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceId()); - } - - /** -- * Can see target. Does not check doors between. -- * @param xValue the x coordinate -- * @param yValue the y coordinate -- * @param zValue the z coordinate -- * @param txValue the target's x coordinate -- * @param tyValue the target's y coordinate -- * @param tzValue the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -+ * Check line of sight from {@link WorldObject} to {@link Location}. -+ * @param origin : The origin object. -+ * @param position : The target position. -+ * @return {@code boolean} : True if object can see position - */ -- public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) -+ public boolean canSeeTarget(WorldObject origin, Location position) - { -- int x = xValue; -- int y = yValue; -- int tx = txValue; -- int ty = tyValue; -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = position.getX(); -+ final int ty = position.getY(); -+ final int tz = position.getZ(); - -- int geoX = getGeoX(x); -- int geoY = getGeoY(y); -- int tGeoX = getGeoX(tx); -- int tGeoY = getGeoY(ty); -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceId(), true)) -+ { -+ return false; -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceId())) -+ { -+ return false; -+ } - -- int z = getNearestZ(geoX, geoY, zValue); -- int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- // Fastpath. -- if ((geoX == tGeoX) && (geoY == tGeoY)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- if (hasGeoPos(tGeoX, tGeoY)) -- { -- return z == tz; -- } - return true; - } - -- if (tz > z) -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) - { -- int tmp = tx; -- tx = x; -- x = tmp; -- -- tmp = ty; -- ty = y; -- y = tmp; -- -- tmp = tz; -- tz = z; -- z = tmp; -- -- tmp = tGeoX; -- tGeoX = geoX; -- geoX = tmp; -- -- tmp = tGeoY; -- tGeoY = geoY; -- geoY = tmp; -+ return goz == gtz; - } - -- final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); -- // First point is guaranteed to be available, skip it, we can always see our own position. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- final int prevZ = pointIter.z(); -- int prevGeoZ = prevZ; -- int ptIndex = 0; -- while (pointIter.next()) -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight(); -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceId()); -+ } -+ -+ /** -+ * Simple check for origin to target visibility. -+ * @param goxValue : origin X geodata coordinate -+ * @param goyValue : origin Y geodata coordinate -+ * @param gozValue : origin Z geodata coordinate -+ * @param oheight : origin height (if instance of {@link Character}) -+ * @param gtxValue : target X geodata coordinate -+ * @param gtyValue : target Y geodata coordinate -+ * @param gtzValue : target Z geodata coordinate -+ * @param theight : target height (if instance of {@link Character}) -+ * @param instanceId -+ * @return {@code boolean} : True, when target can be seen. -+ */ -+ private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, int instanceId) -+ { -+ int goz = gozValue; -+ int gtz = gtzValue; -+ int gox = goxValue; -+ int goy = goyValue; -+ int gtx = gtxValue; -+ int gty = gtyValue; -+ -+ // get line of sight Z coordinates -+ double losoz = goz + ((oheight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ double lostz = gtz + ((theight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ -+ // get X delta and signum -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirox = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; -+ final byte dirtx = sx > 0 ? GeoStructure.CELL_FLAG_W : GeoStructure.CELL_FLAG_E; -+ -+ // get Y delta and signum -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte diroy = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ final byte dirty = sy > 0 ? GeoStructure.CELL_FLAG_N : GeoStructure.CELL_FLAG_S; -+ -+ // get Z delta -+ final int dm = Math.max(dx, dy); -+ final double dz = (lostz - losoz) / dm; -+ -+ // get direction flag for diagonal movement -+ final byte diroxy = getDirXY(dirox, diroy); -+ final byte dirtxy = getDirXY(dirtx, dirty); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte diro; -+ byte dirt; -+ -+ // initialize node values -+ int nox = gox; -+ int noy = goy; -+ int ntx = gtx; -+ int nty = gty; -+ byte nsweo = getNsweNearest(gox, goy, goz); -+ byte nswet = getNsweNearest(gtx, gty, gtz); -+ -+ // loop -+ ABlock block; -+ int index; -+ for (int i = 0; i < ((dm + 1) / 2); i++) -+ { -+ // reset direction flag -+ diro = 0; -+ dirt = 0; - -- if ((curX == prevX) && (curY == prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- continue; -+ // calculate next point XY coordinates -+ d -= dy; -+ d += dx; -+ nox += sx; -+ ntx -= sx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroxy; -+ dirt |= dirtxy; - } -+ else if (e2 > -dy) -+ { -+ // calculate next point X coordinate -+ d -= dy; -+ nox += sx; -+ ntx -= sx; -+ diro |= dirox; -+ dirt |= dirtx; -+ } -+ else if (e2 < dx) -+ { -+ // calculate next point Y coordinate -+ d += dx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroy; -+ dirt |= dirty; -+ } - -- final int beeCurZ = pointIter.z(); -- int curGeoZ = prevGeoZ; -- -- // Check if the position has geodata. -- if (hasGeoPos(curX, curY)) - { -- final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); -- curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); -- int maxHeight; -- if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) -+ // get block of the next cell -+ block = getBlock(nox, noy); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nsweo & diro) == 0) - { -- maxHeight = z + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); - } - else - { -- maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); - } - -- boolean canSeeThrough = false; -- if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) -+ // layer does not exist, return -+ if (index == -1) - { -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else -- { -- canSeeThrough = true; -- } -+ return false; - } - -- if (!canSeeThrough) -+ // get layer and next line of sight Z coordinate -+ goz = block.getHeight(index); -+ losoz += dz; -+ -+ // perform line of sight check, return when fails -+ if ((goz - losoz) > Config.MAX_OBSTACLE_HEIGHT) - { - return false; - } -+ -+ // get layer nswe -+ nsweo = block.getNswe(index); - } -+ { -+ // get block of the next cell -+ block = getBlock(ntx, nty); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nswet & dirt) == 0) -+ { -+ index = block.getIndexAbove(ntx, nty, gtz - GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ else -+ { -+ index = block.getIndexBelow(ntx, nty, gtz + GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ -+ // layer does not exist, return -+ if (index == -1) -+ { -+ return false; -+ } -+ -+ // get layer and next line of sight Z coordinate -+ gtz = block.getHeight(index); -+ lostz -= dz; -+ -+ // perform line of sight check, return when fails -+ if ((gtz - lostz) > Config.MAX_OBSTACLE_HEIGHT) -+ { -+ return false; -+ } -+ -+ // get layer nswe -+ nswet = block.getNswe(index); -+ } - -- prevX = curX; -- prevY = curY; -- prevGeoZ = curGeoZ; -- ++ptIndex; -+ // update coords -+ gox = nox; -+ goy = noy; -+ gtx = ntx; -+ gty = nty; - } - -- return true; -+ // when iteration is completed, compare final Z coordinates -+ return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); - } - - /** -- * Move check. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param zValue the z coordinate -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tzValue the target's z coordinate -- * @param instanceId the instance id -- * @return the last Location (x,y,z) where player can walk - just before wall -+ * Check movement from coordinates to coordinates. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instanceId -+ * @return {code boolean} : True if target coordinates are reachable from origin coordinates - */ -- public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, int instanceId) -+ public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) - { -- final int geoX = getGeoX(x); -- final int geoY = getGeoY(y); -- final int z = getNearestZ(geoX, geoY, zValue); -- final int tGeoX = getGeoX(tx); -- final int tGeoY = getGeoY(ty); -- final int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instanceId, false)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } -- if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instanceId)) -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = z; -+ // perform geodata check -+ final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instanceId); -+ return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); -+ } -+ -+ /** -+ * Check movement from origin to target. Returns last available point in the checked path. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instanceId -+ * @return {@link Location} : Last point where object can walk (just before wall) -+ */ -+ public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) -+ { -+ // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instanceId, false)) -+ { -+ return new Location(ox, oy, oz); -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instanceId)) -+ { -+ return new Location(ox, oy, oz); -+ } - -- while (pointIter.next()) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -- -- if (hasGeoPos(prevX, prevY)) -- { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -- { -- // Can't move, return previous location. -- return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); -- } -- } -- -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return new Location(tx, ty, tz); - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != tz)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- // Different floors, return start location. -- return new Location(x, y, z); -+ return new Location(tx, ty, tz); - } - -- return new Location(tx, ty, tz); -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) -+ { -+ return new Location(tx, ty, tz); -+ } -+ -+ // perform geodata check -+ return checkMove(gox, goy, goz, gtx, gty, gtz, instanceId); - } - - /** -- * Checks if its possible to move from one location to another. -- * @param fromX the X coordinate to start checking from -- * @param fromY the Y coordinate to start checking from -- * @param fromZvalue the Z coordinate to start checking from -- * @param toX the X coordinate to end checking at -- * @param toY the Y coordinate to end checking at -- * @param toZvalue the Z coordinate to end checking at -- * @param instanceId the instance -- * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise -+ * With this method you can check if a position is visible or can be reached by beeline movement.
-+ * Target X and Y reachable and Z is on same floor: -+ *
    -+ *
  • Location of the target with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y reachable but Z is on another floor: -+ *
    -+ *
  • Location of the origin with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y not reachable: -+ *
    -+ *
  • Last accessible location in destination to target.
  • -+ *
-+ * @param gox : origin X geodata coordinate -+ * @param goy : origin Y geodata coordinate -+ * @param goz : origin Z geodata coordinate -+ * @param gtx : target X geodata coordinate -+ * @param gty : target Y geodata coordinate -+ * @param gtz : target Z geodata coordinate -+ * @param instanceId -+ * @return {@link GeoLocation} : The last allowed point of movement. - */ -- public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, int instanceId) -+ protected final GeoLocation checkMove(int gox, int goy, int goz, int gtx, int gty, int gtz, int instanceId) - { -- final int geoX = getGeoX(fromX); -- final int geoY = getGeoY(fromY); -- final int fromZ = getNearestZ(geoX, geoY, fromZvalue); -- final int tGeoX = getGeoX(toX); -- final int tGeoY = getGeoY(toY); -- final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); -- -- if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instanceId, false)) -+ if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instanceId, false)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } -- if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instanceId)) -+ if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instanceId)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = fromZ; -+ // get X delta, signum and direction flag -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirX = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; - -- while (pointIter.next()) -+ // get Y delta, signum and direction flag -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte dirY = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ -+ // get direction flag for diagonal movement -+ final byte dirXY = getDirXY(dirX, dirY); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte direction; -+ -+ // load pointer coordinates -+ int gpx = gox; -+ int gpy = goy; -+ int gpz = goz; -+ -+ // load next pointer -+ int nx = gpx; -+ int ny = gpy; -+ -+ // loop -+ int count = 0; -+ while (count++ < Config.MAX_ITERATIONS) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -+ direction = 0; - -- if (hasGeoPos(prevX, prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -+ d -= dy; -+ d += dx; -+ nx += sx; -+ ny += sy; -+ direction |= dirXY; -+ } -+ else if (e2 > -dy) -+ { -+ d -= dy; -+ nx += sx; -+ direction |= dirX; -+ } -+ else if (e2 < dx) -+ { -+ d += dx; -+ ny += sy; -+ direction |= dirY; -+ } -+ -+ // obstacle found, return -+ if ((getNsweNearest(gpx, gpy, gpz) & direction) == 0) -+ { -+ return new GeoLocation(gpx, gpy, gpz); -+ } -+ -+ // update pointer coordinates -+ gpx = nx; -+ gpy = ny; -+ gpz = getHeightNearest(nx, ny, gpz); -+ -+ // target coordinates reached -+ if ((gpx == gtx) && (gpy == gty)) -+ { -+ if (gpz == gtz) - { -- return false; -+ // path found, Z coordinates are okay, return target point -+ return new GeoLocation(gtx, gty, gtz); - } -+ -+ // path found, Z coordinates are not okay, return last good point -+ return new GeoLocation(gpx, gpy, gpz); - } -+ } -+ -+ return new GeoLocation(gox, goy, goz); -+ } -+ -+ /** -+ * Returns diagonal NSWE flag format of combined two NSWE flags. -+ * @param dirX : X direction NSWE flag -+ * @param dirY : Y direction NSWE flag -+ * @return byte : NSWE flag of combined direction -+ */ -+ private static byte getDirXY(byte dirX, byte dirY) -+ { -+ // check axis directions -+ if (dirY == GeoStructure.CELL_FLAG_N) -+ { -+ if (dirX == GeoStructure.CELL_FLAG_W) -+ { -+ return GeoStructure.CELL_FLAG_NW; -+ } - -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return GeoStructure.CELL_FLAG_NE; - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) -+ if (dirX == GeoStructure.CELL_FLAG_W) - { -- // Different floors. -- return false; -+ return GeoStructure.CELL_FLAG_SW; - } - -- return true; -+ return GeoStructure.CELL_FLAG_SE; - } - -+ /** -+ * Returns the list of location objects as a result of complete path calculation. -+ * @param ox : origin x -+ * @param oy : origin y -+ * @param oz : origin z -+ * @param tx : target x -+ * @param ty : target y -+ * @param tz : target z -+ * @param instanceId -+ * @return {@code List} : complete path from nodes -+ */ -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) -+ { -+ return null; -+ } -+ -+ /** -+ * Returns the instance of the {@link GeoEngine}. -+ * @return {@link GeoEngine} : The instance. -+ */ - public static GeoEngine getInstance() - { - return SingletonHolder.INSTANCE; -@@ -742,6 +933,6 @@ - - private static class SingletonHolder - { -- protected static final GeoEngine INSTANCE = new GeoEngine(); -+ protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); - } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (working copy) -@@ -20,87 +20,83 @@ - import java.util.LinkedList; - import java.util.List; - import java.util.ListIterator; --import java.util.logging.Level; --import java.util.logging.Logger; - - import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; --import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; --import org.l2jmobius.gameserver.model.World; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -+import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -+import org.l2jmobius.gameserver.model.Location; - - /** -- * @author -Nemesiss- -+ * @author Hasha - */ --public class GeoEnginePathfinding -+final class GeoEnginePathfinding extends GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); -+ // pre-allocated buffers -+ private final BufferHolder[] _buffers; - -- private BufferInfo[] _buffers; -- - protected GeoEnginePathfinding() - { -- try -+ super(); -+ -+ final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ _buffers = new BufferHolder[array.length]; -+ int count = 0; -+ for (int i = 0; i < array.length; i++) - { -- final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ final String buf = array[i]; -+ final String[] args = buf.split("x"); - -- _buffers = new BufferInfo[array.length]; -- -- String buf; -- String[] args; -- for (int i = 0; i < array.length; i++) -+ try - { -- buf = array[i]; -- args = buf.split("x"); -- if (args.length != 2) -- { -- throw new Exception("Invalid buffer definition: " + buf); -- } -- -- _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); -+ final int size = Integer.parseInt(args[1]); -+ count += size; -+ _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); - } -+ catch (Exception e) -+ { -+ LOGGER.warning("GeoEnginePathfinding: Can not load buffer setting: " + buf); -+ } - } -- catch (Exception e) -- { -- LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); -- throw new Error("CellPathFinding: load aborted"); -- } -+ -+ LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); - } - -- public boolean pathNodesExist(short regionoffset) -+ @Override -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) - { -- return false; -- } -- -- public List findPath(int x, int y, int z, int tx, int ty, int tz, int instanceId) -- { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -- if (!GeoEngine.getInstance().hasGeo(x, y)) -+ // get origin and check existing geo coords -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { - 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)) -+ -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coords -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { - 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)))); -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // Prepare buffer for pathfinding calculations -+ final NodeBuffer buffer = getBuffer(64 + (2 * Math.max(Math.abs(gox - gtx), Math.abs(goy - gty)))); - if (buffer == null) - { - return null; - } - -- List path = null; -+ // find path -+ List path = null; - try - { -- final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); -- -+ final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); - if (result == null) - { - return null; -@@ -124,33 +120,45 @@ - return path; - } - -- int currentX, currentY, currentZ; -- ListIterator middlePoint; -+ // get path list iterator -+ final ListIterator point = path.listIterator(); - -- middlePoint = path.listIterator(); -- currentX = x; -- currentY = y; -- currentZ = z; -+ // get node A (origin) -+ int nodeAx = gox; -+ int nodeAy = goy; -+ short nodeAz = goz; - -- while (middlePoint.hasNext()) -+ // get node B -+ GeoLocation nodeB = (GeoLocation) point.next(); -+ -+ // iterate thought the path to optimize it -+ int count = 0; -+ while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) - { -- final AbstractNodeLoc locMiddle = middlePoint.next(); -- if (!middlePoint.hasNext()) -- { -- break; -- } -+ // get node C -+ final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - -- final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); -- if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instanceId)) -+ // check movement from node A to node C -+ final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instanceId); -+ if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) - { -- middlePoint.remove(); -+ // can move from node A to node C -+ -+ // remove node B -+ point.remove(); - } - else - { -- currentX = locMiddle.getX(); -- currentY = locMiddle.getY(); -- currentZ = locMiddle.getZ(); -+ // can not move from node A to node C -+ -+ // set node A (node B is part of path, update A coordinates) -+ nodeAx = nodeB.getGeoX(); -+ nodeAy = nodeB.getGeoY(); -+ nodeAz = (short) nodeB.getZ(); - } -+ -+ // set node B -+ nodeB = (GeoLocation) point.next(); - } - - return path; -@@ -157,144 +165,102 @@ - } - - /** -- * Convert geodata position to pathnode position -- * @param geo_pos -- * @return pathnode position -+ * Create list of node locations as result of calculated buffer node tree. -+ * @param node : the entry point -+ * @return List : list of node location - */ -- public short getNodePos(int geo_pos) -+ private static List constructPath(Node node) - { -- return (short) (geo_pos >> 3); // OK? -- } -- -- /** -- * Convert node position to pathnode block position -- * @param node_pos -- * @return pathnode block position (0...255) -- */ -- public short getNodeBlock(int node_pos) -- { -- return (short) (node_pos % 256); -- } -- -- public byte getRegionX(int node_pos) -- { -- return (byte) ((node_pos >> 8) + World.TILE_X_MIN); -- } -- -- public byte getRegionY(int node_pos) -- { -- return (byte) ((node_pos >> 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 node_x rx -- * @return -- */ -- public int calculateWorldX(short node_x) -- { -- return World.MAP_MIN_X + (node_x * 128) + 48; -- } -- -- /** -- * Convert pathnode y to World y position -- * @param node_y -- * @return -- */ -- public int calculateWorldY(short node_y) -- { -- return World.MAP_MIN_Y + (node_y * 128) + 48; -- } -- -- private List constructPath(AbstractNode nodeValue) -- { -- final LinkedList path = new LinkedList<>(); -- int previousDirectionX = Integer.MIN_VALUE; -- int previousDirectionY = Integer.MIN_VALUE; -- int directionX, directionY; -+ // create empty list -+ final LinkedList list = new LinkedList<>(); - -- AbstractNode node = nodeValue; -- while (node.getParent() != null) -+ // set direction X/Y -+ int dx = 0; -+ int dy = 0; -+ -+ // get target parent -+ Node target = node; -+ Node parent = target.getParent(); -+ -+ // while parent exists -+ int count = 0; -+ while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { -- directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); -- directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); -+ // get parent <> target direction X/Y -+ final int nx = parent.getLoc().getGeoX() - target.getLoc().getGeoX(); -+ final int ny = parent.getLoc().getGeoY() - target.getLoc().getGeoY(); - -- // only add a new route point if moving direction changes -- if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) -+ // direction has changed? -+ if ((dx != nx) || (dy != ny)) - { -- previousDirectionX = directionX; -- previousDirectionY = directionY; -+ // add node to the beginning of the list -+ list.addFirst(target.getLoc()); - -- path.addFirst(node.getLoc()); -- node.setLoc(null); -+ // update direction X/Y -+ dx = nx; -+ dy = ny; - } - -- node = node.getParent(); -+ // move to next node, set target and get its parent -+ target = parent; -+ parent = target.getParent(); - } - -- return path; -+ // return list -+ return list; - } - -- private CellNodeBuffer alloc(int size) -+ /** -+ * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer. -+ * @param size : pre-calculated minimal required size -+ * @return NodeBuffer : buffer -+ */ -+ private final NodeBuffer getBuffer(int size) - { -- CellNodeBuffer current = null; -- for (BufferInfo i : _buffers) -+ NodeBuffer current = null; -+ for (BufferHolder holder : _buffers) - { -- if (i.mapSize >= size) -+ // Find proper size of buffer -+ if (holder._size < size) - { -- for (CellNodeBuffer buf : i.buffer) -+ continue; -+ } -+ -+ // Find unlocked NodeBuffer -+ for (NodeBuffer buffer : holder._buffer) -+ { -+ if (!buffer.isLocked()) - { -- if (buf.lock()) -- { -- current = buf; -- break; -- } -+ continue; - } -- if (current != null) -- { -- break; -- } - -- // not found, allocate temporary buffer -- current = new CellNodeBuffer(i.mapSize); -- current.lock(); -- if (i.buffer.size() < i.count) -- { -- i.buffer.add(current); -- break; -- } -+ return buffer; - } -+ -+ // NodeBuffer not found, allocate temporary buffer -+ current = new NodeBuffer(holder._size); -+ current.isLocked(); - } - - return current; - } - -- private static final class BufferInfo -+ /** -+ * NodeBuffer container with specified size and count of separate buffers. -+ */ -+ private static final class BufferHolder - { -- final int mapSize; -- final int count; -- ArrayList buffer; -+ final int _size; -+ List _buffer; - -- public BufferInfo(int size, int cnt) -+ public BufferHolder(int size, int count) - { -- mapSize = size; -- count = cnt; -- buffer = new ArrayList<>(count); -+ _size = size; -+ _buffer = new ArrayList<>(count); -+ for (int i = 0; i < count; i++) -+ { -+ _buffer.add(new NodeBuffer(size)); -+ } - } - } -- -- public static GeoEnginePathfinding getInstance() -- { -- return SingletonHolder.INSTANCE; -- } -- -- private static class SingletonHolder -- { -- protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); -- } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (working copy) -@@ -0,0 +1,197 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @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 height of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getHeightNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first above given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, above given coordinates. -+ */ -+ public abstract short getHeightAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first below given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is closest to given coordinates. -+ * @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 the NSWE flag byte of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getNsweNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first above given coordinates. -+ * @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 getNsweAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first below given coordinates. -+ * @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 getNsweBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is closes layer to given coordinates. -+ * @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. -+ * @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 above given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is first layer below given coordinates. -+ * @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 index to data of the cell, which is first layer below given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexBelowOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeight(int index); -+ -+ /** -+ * Returns the height of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightOriginal(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNswe(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNsweOriginal(int index); -+ -+ /** -+ * Sets the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @param nswe : New NSWE flag byte. -+ */ -+ public abstract void setNswe(int index, byte nswe); -+ -+ /** -+ * Saves the block in L2D format to {@link BufferedOutputStream}. Used only for L2D geodata conversion. -+ * @param stream : The stream. -+ * @throws IOException : Can't save the block to steam. -+ */ -+ public abstract void saveBlock(BufferedOutputStream stream) throws IOException; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (working copy) -@@ -0,0 +1,252 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockComplex(ByteBuffer bb, GeoFormat format) -+ { -+ // initialize buffer -+ _buffer = new byte[GeoStructure.BLOCK_CELLS * 3]; -+ -+ // load data -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // 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); -+ } -+ else -+ { -+ // get nswe -+ final byte nswe = bb.get(); -+ _buffer[i * 3] = nswe; -+ -+ // get height -+ final short height = bb.getShort(); -+ _buffer[(i * 3) + 1] = (byte) (height & 0x00FF); -+ _buffer[(i * 3) + 2] = (byte) (height >> 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height > worldZ ? height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height < worldZ ? height : Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public 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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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 int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_COMPLEX_L2D); -+ -+ // write block data -+ stream.write(_buffer, 0, GeoStructure.BLOCK_CELLS * 3); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (working copy) -@@ -0,0 +1,176 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockFlat extends ABlock -+{ -+ protected final short _height; -+ protected byte _nswe; -+ -+ /** -+ * Creates FlatBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockFlat(ByteBuffer bb, GeoFormat format) -+ { -+ _height = bb.getShort(); -+ _nswe = format != GeoFormat.L2D ? 0x0F : (byte) (0xFF); -+ if (format == GeoFormat.L2OFF) -+ { -+ bb.getShort(); -+ } -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return true; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height > worldZ ? _height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height < worldZ ? _height : Short.MAX_VALUE; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height > worldZ ? _nswe : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height < worldZ ? _nswe : 0; -+ } -+ -+ @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return index -+ return _height < worldZ ? 0 : -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _nswe = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_FLAT_L2D); -+ -+ // write height -+ stream.write((byte) (_height & 0x00FF)); -+ stream.write((byte) (_height >> 8)); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (working copy) -@@ -0,0 +1,465 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+import java.nio.ByteOrder; -+import java.util.Arrays; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockMultilayer extends ABlock -+{ -+ private static final int MAX_LAYERS = Byte.MAX_VALUE; -+ -+ private static ByteBuffer _temp; -+ -+ /** -+ * Initializes the temporarily buffer. -+ */ -+ public static void initialize() -+ { -+ // initialize temporarily buffer and sorting mechanism -+ _temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3); -+ _temp.order(ByteOrder.LITTLE_ENDIAN); -+ } -+ -+ /** -+ * Releases temporarily buffer. -+ */ -+ public static void release() -+ { -+ _temp = null; -+ } -+ -+ protected byte[] _buffer; -+ -+ /** -+ * Implicit constructor for children class. -+ */ -+ protected BlockMultilayer() -+ { -+ _buffer = null; -+ } -+ -+ /** -+ * Creates MultilayerBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockMultilayer(ByteBuffer bb, GeoFormat format) -+ { -+ // 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 = format != GeoFormat.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++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // get data -+ final short data = bb.getShort(); -+ -+ // add nswe and height -+ _temp.put((byte) (data & 0x000F)); -+ _temp.putShort((short) ((short) (data & 0xFFF0) >> 1)); -+ } -+ else -+ { -+ // add nswe -+ _temp.put(bb.get()); -+ -+ // add height -+ _temp.putShort(bb.getShort()); -+ } -+ } -+ } -+ -+ // 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 height -+ if (height > worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, return minimum value -+ return Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 height -+ if (height < worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, return maximum value -+ return Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 nswe -+ if (height > worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 nswe -+ if (height < worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @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 first layer data (first from bottom) -+ byte layers = _buffer[index++]; -+ -+ // loop though all cell layers, find closest layer -+ 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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ // get height -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(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]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ // get nswe -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ // set nswe -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_MULTILAYER_L2D); -+ -+ // for each cell -+ int index = 0; -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ // write layers count -+ final byte layers = _buffer[index++]; -+ stream.write(layers); -+ -+ // write cell data -+ stream.write(_buffer, index, layers * 3); -+ -+ // move index to next cell -+ index += layers * 3; -+ } -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (working copy) -@@ -0,0 +1,150 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockNull extends ABlock -+{ -+ private final byte _nswe; -+ -+ public BlockNull() -+ { -+ _nswe = (byte) 0xFF; -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return false; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweBelow(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) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) -+ { -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (nonexistent) -@@ -1,48 +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 . -- */ --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() -- { -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (nonexistent) -@@ -1,77 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (nonexistent) -@@ -1,56 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (working copy) -@@ -0,0 +1,39 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public enum GeoFormat -+{ -+ L2J("%d_%d.l2j"), -+ L2OFF("%d_%d_conv.dat"), -+ L2D("%d_%d.l2d"); -+ -+ private final String _filename; -+ -+ private GeoFormat(String filename) -+ { -+ _filename = filename; -+ } -+ -+ public String getFilename() -+ { -+ return _filename; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (working copy) -@@ -0,0 +1,67 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.geoengine.GeoEngine; -+import org.l2jmobius.gameserver.model.Location; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoLocation extends Location -+{ -+ private byte _nswe; -+ -+ public GeoLocation(int x, int y, int z) -+ { -+ super(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public void set(int x, int y, short z) -+ { -+ super.setXYZ(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public int getGeoX() -+ { -+ return _x; -+ } -+ -+ public int getGeoY() -+ { -+ return _y; -+ } -+ -+ @Override -+ public int getX() -+ { -+ return GeoEngine.getWorldX(_x); -+ } -+ -+ @Override -+ public int getY() -+ { -+ return GeoEngine.getWorldY(_y); -+ } -+ -+ public byte getNSWE() -+ { -+ return _nswe; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (working copy) -@@ -0,0 +1,70 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoStructure -+{ -+ // cells -+ public static final byte CELL_FLAG_E = 1 << 0; -+ public static final byte CELL_FLAG_W = 1 << 1; -+ public static final byte CELL_FLAG_S = 1 << 2; -+ public static final byte CELL_FLAG_N = 1 << 3; -+ public static final byte CELL_FLAG_SE = 1 << 4; -+ public static final byte CELL_FLAG_SW = 1 << 5; -+ public static final byte CELL_FLAG_NE = 1 << 6; -+ public static final byte CELL_FLAG_NW = (byte) (1 << 7); -+ -+ public static final int CELL_HEIGHT = 8; -+ public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6; -+ -+ // blocks -+ public static final byte TYPE_FLAT_L2J_L2OFF = 0; -+ public static final byte TYPE_FLAT_L2D = (byte) 0xD0; -+ public static final byte TYPE_COMPLEX_L2J = 1; -+ public static final byte TYPE_COMPLEX_L2OFF = 0x40; -+ public static final byte TYPE_COMPLEX_L2D = (byte) 0xD1; -+ 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) -+ public static final byte TYPE_MULTILAYER_L2D = (byte) 0xD2; -+ -+ 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; -+ -+ // regions -+ 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; -+ -+ // global geodata -+ private static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); -+ private 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; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (nonexistent) -@@ -1,42 +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 . -- */ --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); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (working copy) -@@ -0,0 +1,53 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public interface IGeoObject -+{ -+ /** -+ * Returns geodata X coordinate of the {@link IGeoObject}. -+ * @return int : Geodata X coordinate. -+ */ -+ int getGeoX(); -+ -+ /** -+ * Returns geodata Y coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Y coordinate. -+ */ -+ int getGeoY(); -+ -+ /** -+ * Returns geodata Z coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Z coordinate. -+ */ -+ int getGeoZ(); -+ -+ /** -+ * Returns height of the {@link IGeoObject}. -+ * @return int : Height. -+ */ -+ int getHeight(); -+ -+ /** -+ * Returns {@link IGeoObject} data. -+ * @return byte[][] : {@link IGeoObject} data. -+ */ -+ byte[][] getObjectGeoData(); -+} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (nonexistent) -@@ -1,47 +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 . -- */ --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(); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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); -- -- // 1 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (nonexistent) -@@ -1,55 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --/** -- * @author HorridoJoho -- */ --public final class NullRegion implements IRegion --{ -- public static final NullRegion INSTANCE = new NullRegion(); -- -- @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 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Region.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (nonexistent) -@@ -1,92 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (nonexistent) -@@ -1,87 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public abstract class AbstractNode --{ -- private T _loc; -- private AbstractNode _parent; -- -- public AbstractNode(T loc) -- { -- _loc = loc; -- } -- -- public void setParent(AbstractNode p) -- { -- _parent = p; -- } -- -- public AbstractNode getParent() -- { -- return _parent; -- } -- -- public T getLoc() -- { -- return _loc; -- } -- -- public void setLoc(T l) -- { -- _loc = l; -- } -- -- @Override -- public int hashCode() -- { -- final int prime = 31; -- int result = 1; -- result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); -- return result; -- } -- -- @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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (nonexistent) -@@ -1,33 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --/** -- * @author -Nemesiss- -- */ --public abstract class AbstractNodeLoc --{ -- public abstract int getX(); -- -- public abstract int getY(); -- -- public abstract int getZ(); -- -- public abstract int getNodeX(); -- -- public abstract int getNodeY(); --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (nonexistent) -@@ -1,67 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public class CellNode extends AbstractNode --{ -- 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (nonexistent) -@@ -1,348 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --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 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) -- { -- _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(); -- } -- -- public final List debugPath() -- { -- final List 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 void getNeighbors() -- { -- if (!_current.getLoc().canGoAll()) -- { -- 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); -- } -- -- // SouthEast -- if ((nodeE != null) && (nodeS != null)) -- { -- if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) -- { -- addNode(x + 1, y + 1, z, true); -- } -- } -- -- // SouthWest -- if ((nodeS != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) -- { -- addNode(x - 1, y + 1, z, true); -- } -- } -- -- // NorthEast -- if ((nodeN != null) && (nodeE != null)) -- { -- if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) -- { -- addNode(x + 1, y - 1, z, true); -- } -- } -- -- // NorthWest -- if ((nodeN != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) -- { -- addNode(x - 1, y - 1, z, true); -- } -- } -- } -- -- private 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(); -- // reinit node if needed -- if (result.getLoc() != null) -- { -- result.getLoc().set(x, y, z); -- } -- else -- { -- result.setLoc(new NodeLoc(x, y, z)); -- } -- } -- -- return result; -- } -- -- private 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 boolean isHighWeight(int x, int y, int z) -- { -- final CellNode result = getNode(x, y, z); -- if (result == null) -- { -- return true; -- } -- -- if (!result.getLoc().canGoAll()) -- { -- return true; -- } -- if (Math.abs(result.getLoc().getZ() - z) > 16) -- { -- return true; -- } -- -- return false; -- } -- -- private 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (working copy) -@@ -0,0 +1,87 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+ -+/** -+ * @author Hasha -+ */ -+public class Node -+{ -+ // node coords and nswe flag -+ private GeoLocation _loc; -+ -+ // node parent (for reverse path construction) -+ private Node _parent; -+ // node child (for moving over nodes during iteration) -+ private Node _child; -+ -+ // node G cost (movement cost = parent movement cost + current movement cost) -+ private double _cost = -1000; -+ -+ public void setLoc(int x, int y, int z) -+ { -+ _loc = new GeoLocation(x, y, z); -+ } -+ -+ public GeoLocation getLoc() -+ { -+ return _loc; -+ } -+ -+ public void setParent(Node parent) -+ { -+ _parent = parent; -+ } -+ -+ public Node getParent() -+ { -+ return _parent; -+ } -+ -+ public void setChild(Node child) -+ { -+ _child = child; -+ } -+ -+ public Node getChild() -+ { -+ return _child; -+ } -+ -+ public void setCost(double cost) -+ { -+ _cost = cost; -+ } -+ -+ public double getCost() -+ { -+ return _cost; -+ } -+ -+ public void free() -+ { -+ // reset node location -+ _loc = null; -+ -+ // reset node parent, child and cost -+ _parent = null; -+ _child = null; -+ _cost = -1000; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (working copy) -@@ -0,0 +1,306 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import java.util.concurrent.locks.ReentrantLock; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+ -+/** -+ * @author DS, Hasha; Credits to Diamond -+ */ -+public class NodeBuffer -+{ -+ private final ReentrantLock _lock = new ReentrantLock(); -+ private final int _size; -+ private final Node[][] _buffer; -+ -+ // center coordinates -+ private int _cx = 0; -+ private int _cy = 0; -+ -+ // target coordinates -+ private int _gtx = 0; -+ private int _gty = 0; -+ private short _gtz = 0; -+ -+ private Node _current = null; -+ -+ /** -+ * Constructor of NodeBuffer. -+ * @param size : one dimension size of buffer -+ */ -+ public NodeBuffer(int size) -+ { -+ // set size -+ _size = size; -+ -+ // initialize buffer -+ _buffer = new Node[size][size]; -+ for (int x = 0; x < size; x++) -+ { -+ for (int y = 0; y < size; y++) -+ { -+ _buffer[x][y] = 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 Node : first node of path -+ */ -+ public Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) -+ { -+ // set coordinates (middle of the line (gox,goy) - (gtx,gty), will be in the center of the buffer) -+ _cx = gox + ((gtx - gox - _size) / 2); -+ _cy = goy + ((gty - goy - _size) / 2); -+ -+ _gtx = gtx; -+ _gty = gty; -+ _gtz = gtz; -+ -+ _current = getNode(gox, goy, goz); -+ _current.setCost(getCostH(gox, goy, goz)); -+ -+ int count = 0; -+ do -+ { -+ // reached target? -+ if ((_current.getLoc().getGeoX() == _gtx) && (_current.getLoc().getGeoY() == _gty) && (Math.abs(_current.getLoc().getZ() - _gtz) < 8)) -+ { -+ return _current; -+ } -+ -+ // expand current node -+ expand(); -+ -+ // move pointer -+ _current = _current.getChild(); -+ } -+ while ((_current != null) && (count++ < Config.MAX_ITERATIONS)); -+ -+ return null; -+ } -+ -+ public boolean isLocked() -+ { -+ return _lock.tryLock(); -+ } -+ -+ public void free() -+ { -+ _current = null; -+ -+ for (Node[] nodes : _buffer) -+ { -+ for (Node node : nodes) -+ { -+ if (node.getLoc() != null) -+ { -+ node.free(); -+ } -+ } -+ } -+ -+ _lock.unlock(); -+ } -+ -+ /** -+ * Check _current Node and add its neighbors to the buffer. -+ */ -+ private final void expand() -+ { -+ // can't move anywhere, don't expand -+ final byte nswe = _current.getLoc().getNSWE(); -+ if (nswe == 0) -+ { -+ return; -+ } -+ -+ // get geo coords of the node to be expanded -+ final int x = _current.getLoc().getGeoX(); -+ final int y = _current.getLoc().getGeoY(); -+ final short z = (short) _current.getLoc().getZ(); -+ -+ // can move north, expand -+ if ((nswe & GeoStructure.CELL_FLAG_N) != 0) -+ { -+ addNode(x, y - 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move south, expand -+ if ((nswe & GeoStructure.CELL_FLAG_S) != 0) -+ { -+ addNode(x, y + 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_W) != 0) -+ { -+ addNode(x - 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_E) != 0) -+ { -+ addNode(x + 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move north-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NW) != 0) -+ { -+ addNode(x - 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move north-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NE) != 0) -+ { -+ addNode(x + 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SW) != 0) -+ { -+ addNode(x - 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SE) != 0) -+ { -+ addNode(x + 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ } -+ -+ /** -+ * Returns node, if it exists in buffer. -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param z : node Z coord -+ * @return Node : node, if exits in buffer -+ */ -+ private final Node getNode(int x, int y, short z) -+ { -+ // check node X out of coordinates -+ final int ix = x - _cx; -+ if ((ix < 0) || (ix >= _size)) -+ { -+ return null; -+ } -+ -+ // check node Y out of coordinates -+ final int iy = y - _cy; -+ if ((iy < 0) || (iy >= _size)) -+ { -+ return null; -+ } -+ -+ // get node -+ final Node result = _buffer[ix][iy]; -+ -+ // check and update -+ if (result.getLoc() == null) -+ { -+ result.setLoc(x, y, z); -+ } -+ -+ // return node -+ return result; -+ } -+ -+ /** -+ * Add node given by coordinates to the buffer. -+ * @param x : geo X coord -+ * @param y : geo Y coord -+ * @param z : geo Z coord -+ * @param weight : weight of movement to new node -+ */ -+ private final void addNode(int x, int y, short z, int weight) -+ { -+ // get node to be expanded -+ final Node node = getNode(x, y, z); -+ if (node == null) -+ { -+ return; -+ } -+ -+ // Z distance between nearby cells is higher than cell size -+ if (node.getLoc().getZ() > (z + (2 * GeoStructure.CELL_HEIGHT))) -+ { -+ return; -+ } -+ -+ // node was already expanded, return -+ if (node.getCost() >= 0) -+ { -+ return; -+ } -+ -+ node.setParent(_current); -+ if (node.getLoc().getNSWE() != (byte) 0xFF) -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + (weight * Config.OBSTACLE_MULTIPLIER)); -+ } -+ else -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + weight); -+ } -+ -+ Node current = _current; -+ int count = 0; -+ while ((current.getChild() != null) && (count < (Config.MAX_ITERATIONS * 4))) -+ { -+ count++; -+ if (current.getChild().getCost() > node.getCost()) -+ { -+ node.setChild(current.getChild()); -+ break; -+ } -+ current = current.getChild(); -+ } -+ -+ if (count >= (Config.MAX_ITERATIONS * 4)) -+ { -+ System.err.println("Pathfinding: too long loop detected, cost:" + node.getCost()); -+ } -+ -+ current.setChild(node); -+ } -+ -+ /** -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param i : node Z coord -+ * @return double : node cost -+ */ -+ private final double getCostH(int x, int y, int i) -+ { -+ final int dX = x - _gtx; -+ final int dY = y - _gty; -+ final int dZ = (i - _gtz) / GeoStructure.CELL_HEIGHT; -+ -+ // return (Math.abs(dX) + Math.abs(dY) + Math.abs(dZ)) * Config.HEURISTIC_WEIGHT; // Manhattan distance -+ return Math.sqrt((dX * dX) + (dY * dY) + (dZ * dZ)) * Config.HEURISTIC_WEIGHT; // Direct distance -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (revision 8319) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -- --/** -- * @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 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 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; -- // return super.hashCode(); -- } -- -- @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; -- if (_x != other._x) -- { -- return false; -- } -- if (_y != other._y) -- { -- return false; -- } -- if (_goNorth != other._goNorth) -- { -- return false; -- } -- if (_goEast != other._goEast) -- { -- return false; -- } -- if (_goSouth != other._goSouth) -- { -- return false; -- } -- if (_goWest != other._goWest) -- { -- return false; -- } -- if (_geoHeight != other._geoHeight) -- { -- return false; -- } -- return true; -- } --} -Index: java/org/l2jmobius/gameserver/model/actor/Creature.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/Creature.java (revision 8399) -+++ java/org/l2jmobius/gameserver/model/actor/Creature.java (working copy) -@@ -57,8 +57,6 @@ - import org.l2jmobius.gameserver.enums.Team; - import org.l2jmobius.gameserver.enums.TeleportWhereType; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; - import org.l2jmobius.gameserver.instancemanager.IdManager; - import org.l2jmobius.gameserver.instancemanager.InstanceManager; - import org.l2jmobius.gameserver.instancemanager.MapRegionManager; -@@ -3362,7 +3360,7 @@ - - public boolean disregardingGeodata; - public int onGeodataPathIndex; -- public List geoPath; -+ public List geoPath; - public int geoPathAccurateTx; - public int geoPathAccurateTy; - public int geoPathGtx; -@@ -4386,7 +4384,7 @@ - if (((originalDistance - distance) > 30) && !isAfraid() && !isInVehicle) - { - // Path calculation -- overrides previous movement check -- m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId()); -+ m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId()); - if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found - { - if (isPlayer() && !_isFlying && !isInWater) -Index: java/org/l2jmobius/gameserver/model/actor/Summon.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/Summon.java (revision 8319) -+++ java/org/l2jmobius/gameserver/model/actor/Summon.java (working copy) -@@ -29,7 +29,6 @@ - import org.l2jmobius.gameserver.enums.ShotType; - import org.l2jmobius.gameserver.enums.Team; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; - import org.l2jmobius.gameserver.handler.IItemHandler; - import org.l2jmobius.gameserver.handler.ItemHandler; - import org.l2jmobius.gameserver.instancemanager.TerritoryWarManager; -@@ -646,7 +645,7 @@ - return false; - } - -- if ((this != target) && skill.isPhysical() && Config.PATHFINDING && (GeoEnginePathfinding.getInstance().findPath(getX(), getY(), getZ(), target.getX(), target.getY(), target.getZ(), getInstanceId()) == null)) -+ if ((this != target) && skill.isPhysical() && Config.PATHFINDING && (GeoEngine.getInstance().findPath(getX(), getY(), getZ(), target.getX(), target.getY(), target.getZ(), getInstanceId()) == null)) - { - sendPacket(SystemMessageId.CANNOT_SEE_TARGET); - return false; -Index: java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (revision 8352) -+++ java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (working copy) -@@ -13406,7 +13406,7 @@ - { - if (Config.CORRECT_PLAYER_Z) - { -- final int nearestZ = GeoEngine.getInstance().getHigherHeight(getX(), getY(), getZ()); -+ final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); - if (getZ() < nearestZ) - { - teleToLocation(new Location(getX(), getY(), nearestZ)); -Index: java/org/l2jmobius/gameserver/util/GeoUtils.java -=================================================================== ---- java/org/l2jmobius/gameserver/util/GeoUtils.java (revision 8319) -+++ java/org/l2jmobius/gameserver/util/GeoUtils.java (working copy) -@@ -19,7 +19,7 @@ - import java.awt.Color; - - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; - import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; - -@@ -26,25 +26,25 @@ - /** - * @author HorridoJoho - */ --public final class GeoUtils -+public class GeoUtils - { - public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); - - final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); - - while (iter.next()) - { -- final int wx = GeoEngine.getInstance().getWorldX(iter.x()); -- final int wy = GeoEngine.getInstance().getWorldY(iter.y()); -+ final int wx = GeoEngine.getWorldX(iter.x()); -+ final int wy = GeoEngine.getWorldY(iter.y()); - - prim.addPoint(Color.RED, wx, wy, z); - } -@@ -53,21 +53,21 @@ - - public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); -- int wy = GeoEngine.getInstance().getWorldY(prevY); -+ int wx = GeoEngine.getWorldX(prevX); -+ int wy = GeoEngine.getWorldY(prevY); - int wz = iter.z(); - prim.addPoint(Color.RED, wx, wy, wz); - -@@ -78,8 +78,8 @@ - - if ((curX != prevX) || (curY != prevY)) - { -- wx = GeoEngine.getInstance().getWorldX(curX); -- wy = GeoEngine.getInstance().getWorldY(curY); -+ wx = GeoEngine.getWorldX(curX); -+ wy = GeoEngine.getWorldY(curY); - wz = iter.z(); - - prim.addPoint(Color.RED, wx, wy, wz); -@@ -93,7 +93,7 @@ - - private static Color getDirectionColor(int x, int y, int z, int nswe) - { -- if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) -+ if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) - { - return Color.GREEN; - } -@@ -109,9 +109,8 @@ - int iPacket = 0; - - ExServerPrimitive exsp = null; -- final GeoEngine ge = GeoEngine.getInstance(); -- final int playerGx = ge.getGeoX(player.getX()); -- final int playerGy = ge.getGeoY(player.getY()); -+ final int playerGx = GeoEngine.getGeoX(player.getX()); -+ final int playerGy = GeoEngine.getGeoY(player.getY()); - for (int dx = -geoRadius; dx <= geoRadius; ++dx) - { - for (int dy = -geoRadius; dy <= geoRadius; ++dy) -@@ -135,12 +134,12 @@ - final int gx = playerGx + dx; - final int gy = playerGy + dy; - -- final int x = ge.getWorldX(gx); -- final int y = ge.getWorldY(gy); -- final int z = ge.getNearestZ(gx, gy, player.getZ()); -+ final int x = GeoEngine.getWorldX(gx); -+ final int y = GeoEngine.getWorldY(gy); -+ final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); - - // north arrow -- Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); -+ Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); - 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); -@@ -147,7 +146,7 @@ - exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z); - - // east arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); - 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); -@@ -154,13 +153,13 @@ - exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z); - - // south arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); - 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, Cell.NSWE_WEST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); - 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); -@@ -188,15 +187,15 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_EAST; -+ return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_EAST; -+ return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; - } - else - { -- return Cell.NSWE_EAST; -+ return GeoStructure.CELL_FLAG_E; // Direction.EAST; - } - } - else if (x < lastX) // west -@@ -203,26 +202,27 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_WEST; -+ return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_WEST; -+ return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; - } - else - { -- return Cell.NSWE_WEST; -+ return GeoStructure.CELL_FLAG_W; // Direction.WEST; - } - } -- else // unchanged x -+ else -+ // unchanged x - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH; -+ return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH; -+ return GeoStructure.CELL_FLAG_N; // Direction.NORTH; - } - else - { -Index: java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java -=================================================================== ---- java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (nonexistent) -+++ java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (working copy) -@@ -0,0 +1,386 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.tools.geodataconverter; -+ -+import java.io.BufferedOutputStream; -+import java.io.File; -+import java.io.FileOutputStream; -+import java.io.RandomAccessFile; -+import java.nio.ByteOrder; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.Scanner; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.commons.util.PropertiesParser; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoDataConverter -+{ -+ private static GeoFormat _format; -+ private static ABlock[][] _blocks; -+ -+ public static void main(String[] args) -+ { -+ // initialize config -+ loadGeoengineConfigs(); -+ -+ // get geodata format -+ String type = ""; -+ try (Scanner scn = new Scanner(System.in)) -+ { -+ while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) -+ { -+ System.out.println("GeoDataConverter: Select source geodata type:"); -+ System.out.println(" J: L2J (e.g. 23_22.l2j)"); -+ System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); -+ System.out.println(" E: Exit"); -+ System.out.print("Choice: "); -+ type = scn.next(); -+ } -+ } -+ if (type.equalsIgnoreCase("E")) -+ { -+ System.exit(0); -+ } -+ -+ _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; -+ -+ // start conversion -+ System.out.println("GeoDataConverter: Converting all " + _format + " files."); -+ -+ // initialize geodata container -+ _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files -+ int converted = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) -+ { -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) -+ { -+ final String input = String.format(_format.getFilename(), rx, ry); -+ final String filepath = Config.GEODATA_PATH; -+ final File f = new File(filepath + input); -+ if (f.exists() && !f.isDirectory()) -+ { -+ // load geodata -+ if (!loadGeoBlocks(input)) -+ { -+ System.out.println("GeoDataConverter: Unable to load " + input + " region file."); -+ continue; -+ } -+ -+ // recalculate nswe -+ if (!recalculateNswe()) -+ { -+ System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); -+ continue; -+ } -+ -+ // save geodata -+ final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); -+ if (!saveGeoBlocks(output)) -+ { -+ System.out.println("GeoDataConverter: Unable to save " + output + " region file."); -+ continue; -+ } -+ -+ converted++; -+ System.out.println("GeoDataConverter: Created " + output + " region file."); -+ } -+ } -+ } -+ System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); -+ } -+ -+ /** -+ * Loads geo blocks from buffer of the region file. -+ * @param filename : The name of the to load. -+ * @return boolean : True when successful. -+ */ -+ private static boolean loadGeoBlocks(String filename) -+ { -+ // region file is load-able, try to load it -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) -+ { -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) -+ if (_format == GeoFormat.L2OFF) -+ { -+ for (int i = 0; i < 18; i++) -+ { -+ buffer.get(); -+ } -+ } -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ if (_format == GeoFormat.L2J) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2J: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2J: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ else -+ { -+ // get block type -+ final short type = buffer.getShort(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ default: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ if (buffer.remaining() > 0) -+ { -+ System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ return false; -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); -+ return false; -+ } -+ } -+ -+ /** -+ * Recalculate diagonal flags for the region file. -+ * @return boolean : True when successful. -+ */ -+ private static boolean recalculateNswe() -+ { -+ try -+ { -+ for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) -+ { -+ for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) -+ { -+ // get block -+ final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // skip flat blocks -+ if (block instanceof BlockFlat) -+ { -+ continue; -+ } -+ -+ // for complex and multilayer blocks go though all layers -+ short height = Short.MAX_VALUE; -+ int index; -+ while ((index = block.getIndexBelow(x, y, height)) != -1) -+ { -+ // get height and nswe -+ height = block.getHeight(index); -+ byte nswe = block.getNswe(index); -+ -+ // update nswe with diagonal flags -+ nswe = updateNsweBelow(x, y, height, nswe); -+ -+ // set nswe of the cell -+ block.setNswe(index, nswe); -+ } -+ } -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ /** -+ * Updates the NSWE flag with diagonal flags. -+ * @param x : Geodata X coordinate. -+ * @param y : Geodata Y coordinate. -+ * @param z : Geodata Z coordinate. -+ * @param nsweValue : NSWE flag to be updated. -+ * @return byte : Updated NSWE flag. -+ */ -+ private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) -+ { -+ byte nswe = nsweValue; -+ -+ // calculate virtual layer height -+ final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); -+ -+ // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) -+ final byte nsweN = getNsweBelow(x, y - 1, height); -+ final byte nsweS = getNsweBelow(x, y + 1, height); -+ final byte nsweW = getNsweBelow(x - 1, y, height); -+ final byte nsweE = getNsweBelow(x + 1, y, height); -+ -+ // north-west -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NW; -+ } -+ -+ // north-east -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NE; -+ } -+ -+ // south-west -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SW; -+ } -+ -+ // south-east -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SE; -+ } -+ -+ return nswe; -+ } -+ -+ private static byte getNsweBelow(int geoX, int geoY, short worldZ) -+ { -+ // out of geo coordinates -+ if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) -+ { -+ return 0; -+ } -+ -+ // out of geo coordinates -+ if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) -+ { -+ return 0; -+ } -+ -+ // get block -+ final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // get index, when valid, return nswe -+ final int index = block.getIndexBelow(geoX, geoY, worldZ); -+ return index == -1 ? 0 : block.getNswe(index); -+ } -+ -+ /** -+ * Save region file to file. -+ * @param filename : The name of file to save. -+ * @return boolean : True when successful. -+ */ -+ private static boolean saveGeoBlocks(String filename) -+ { -+ try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) -+ { -+ // loop over region blocks and save each block -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[ix][iy].saveBlock(bos); -+ } -+ } -+ -+ // flush data to file -+ bos.flush(); -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ private static void loadGeoengineConfigs() -+ { -+ final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); -+ Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); -+ Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); -+ Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); -+ Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); -+ Config.PATHFINDING = geoData.getBoolean("PathFinding", true); -+ Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -+ Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); -+ Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); -+ Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); -+ Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); -+ Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); -+ } -+} -\ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/geodata/Readme.txt b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/geodata/Readme.txt index a480c8c380..2611882e56 100644 --- a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/geodata/Readme.txt +++ b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/geodata/Readme.txt @@ -23,8 +23,8 @@ a - Prerequisites b - Make it work ---------------------------------------------- -To make geodata working: -* unpack your geodata files into "/data/geodata" folder -* open "/config/GeoEngine.ini" with your favorite text editor and then edit following config: - - CoordSynchronize = 2 -* If you do not use any geodata files, the server will automatically change this setting to -1. +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. diff --git a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java index d5884ea7b8..d210def899 100644 --- a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java +++ b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java @@ -270,7 +270,7 @@ public class PrimevalIsle extends AbstractNpcAI final double cos = Math.cos(radian); final int newX = (int) (npc.getX() + (cos * distance)); final int newY = (int) (npc.getY() + (sin * distance)); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, npc.getZ(), npc.getInstanceId()); + final Location loc = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, npc.getZ(), npc.getInstanceId()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, loc, 0); } else if (ag_type == 1) diff --git a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/ai/others/FleeMonsters.java b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/ai/others/FleeMonsters.java index 906ef8e8b8..3bab5a3ad5 100644 --- a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/ai/others/FleeMonsters.java +++ b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/ai/others/FleeMonsters.java @@ -69,7 +69,7 @@ public class FleeMonsters extends AbstractNpcAI final int posX = (int) (npc.getX() + (FLEE_DISTANCE * Math.cos(radians))); final int posY = (int) (npc.getY() + (FLEE_DISTANCE * Math.sin(radians))); final int posZ = npc.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, attacker.getInstanceId()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, npc.getInstanceId()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); return super.onAttack(npc, attacker, damage, isSummon); } diff --git a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index 524652cbc1..18511c6541 100644 --- a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { @@ -133,8 +133,8 @@ public class AdminGeodata implements IAdminCommandHandler } case "admin_geomap": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y + " (" + ((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + "," + ((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + " to " + ((((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + "," + ((((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + ")"); break; } diff --git a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index a29a86b4f4..ad6fcd2b7a 100644 --- a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -19,9 +19,9 @@ package handlers.admincommandhandlers; import java.util.List; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; +import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.util.BuilderUtil; @@ -44,13 +44,13 @@ public class AdminPathNode implements IAdminCommandHandler } if (activeChar.getTarget() != null) { - final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceId()); + final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceId()); if (path == null) { BuilderUtil.sendSysMessage(activeChar, "No Route!"); return true; } - for (AbstractNodeLoc a : path) + for (Location a : path) { BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } diff --git a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/Blink.java b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/Blink.java index d2d82c3bd0..25c96e50ff 100644 --- a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/Blink.java +++ b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/Blink.java @@ -68,7 +68,7 @@ public class Blink extends AbstractEffect final int x = effected.getX() + x1; final int y = effected.getY() + y1; final int z = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceId()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceId()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, destination, FlyType.DUMMY)); effected.abortAttack(); diff --git a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/EnemyCharge.java b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/EnemyCharge.java index e0d12253d3..53041adad7 100644 --- a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/EnemyCharge.java +++ b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/EnemyCharge.java @@ -90,7 +90,7 @@ public class EnemyCharge extends AbstractEffect final int x = curX + (int) ((distance - offset) * cos); final int y = curY + (int) ((distance - offset) * sin); final int z = info.getEffected().getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(info.getEffector().getX(), info.getEffector().getY(), info.getEffector().getZ(), x, y, z, info.getEffector().getInstanceId()); + final Location destination = GeoEngine.getInstance().getValidLocation(info.getEffector().getX(), info.getEffector().getY(), info.getEffector().getZ(), x, y, z, info.getEffector().getInstanceId()); info.getEffector().broadcastPacket(new FlyToLocation(info.getEffector(), destination, FlyType.CHARGE)); // maybe is need force set X,Y,Z diff --git a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/Fishing.java b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/Fishing.java index 3b9a284fbd..c7da046bc7 100644 --- a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/Fishing.java +++ b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/Fishing.java @@ -236,7 +236,7 @@ public class Fishing extends AbstractEffect // always use water zone, fishing zone high z is high in the air... final int baitZ = waterZone.getWaterZ(); - if (!GeoEngine.getInstance().canSeeTarget(player, new Location(baitX, baitY, baitZ))) + if (!GeoEngine.getInstance().canSeeLocation(player, new Location(baitX, baitY, baitZ))) { return Integer.MIN_VALUE; } diff --git a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java index 3e313dda32..71fcae3db6 100644 --- a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java +++ b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java @@ -82,7 +82,7 @@ public class TeleportToTarget extends AbstractEffect final int x = (int) (px + (25 * Math.cos(ph))); final int y = (int) (py + (25 * Math.sin(ph))); final int z = target.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceId()); + final Location loc = GeoEngine.getInstance().getValidLocation(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceId()); creature.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); creature.broadcastPacket(new FlyToLocation(creature, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); creature.abortAttack(); diff --git a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/ThrowUp.java b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/ThrowUp.java index a6d06f9496..f1616cbeed 100644 --- a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/ThrowUp.java +++ b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/ThrowUp.java @@ -93,7 +93,7 @@ public class ThrowUp extends AbstractEffect final int x = info.getEffector().getX() - (int) (offset * cos); final int y = info.getEffector().getY() - (int) (offset * sin); final int z = info.getEffected().getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(info.getEffected().getX(), info.getEffected().getY(), info.getEffected().getZ(), x, y, z, info.getEffected().getInstanceId()); + final Location destination = GeoEngine.getInstance().getValidLocation(info.getEffected().getX(), info.getEffected().getY(), info.getEffected().getZ(), x, y, z, info.getEffected().getInstanceId()); info.getEffected().broadcastPacket(new FlyToLocation(info.getEffected(), destination, FlyType.THROW_UP)); // TODO: Review. info.getEffected().setXYZ(destination); diff --git a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java index 35cf9c3121..ddea5da753 100644 --- a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java +++ b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java @@ -65,7 +65,7 @@ public class RollingDice implements IItemHandler final int x = player.getX() + x1; final int y = player.getY() + y1; final int z = player.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceId()); + final Location destination = GeoEngine.getInstance().getValidLocation(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceId()); Broadcast.toSelfAndKnownPlayers(player, new Dice(player.getObjectId(), itemId, number, destination.getX(), destination.getY(), destination.getZ())); final SystemMessage sm = new SystemMessage(SystemMessageId.C1_HAS_ROLLED_A_S2); diff --git a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/instances/CrystalCaverns/CrystalCaverns.java b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/instances/CrystalCaverns/CrystalCaverns.java index 2dd1b5b562..c11c6db437 100644 --- a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/instances/CrystalCaverns/CrystalCaverns.java +++ b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/instances/CrystalCaverns/CrystalCaverns.java @@ -677,7 +677,7 @@ public class CrystalCaverns extends AbstractInstance final int _x = effector.getX() - (int) (offset * cos); final int _y = effector.getY() - (int) (offset * sin); final int _z = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), _x, _y, _z, effected.getInstanceId()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), _x, _y, _z, effected.getInstanceId()); effected.broadcastPacket(new FlyToLocation(effected, destination, FlyType.THROW_UP)); // maybe is need force set X,Y,Z diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/Config.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/Config.java index 8eb6817633..0de24c6906 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/Config.java @@ -59,6 +59,7 @@ import org.l2jmobius.commons.enums.ServerMode; import org.l2jmobius.commons.util.IXmlReader; import org.l2jmobius.commons.util.PropertiesParser; import org.l2jmobius.gameserver.enums.ChatType; +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; @@ -1060,12 +1061,17 @@ public class Config // GeoEngine // -------------------------------------------------- public static Path GEODATA_PATH; + public static GeoType GEODATA_TYPE; public static boolean PATHFINDING; public static String PATHFIND_BUFFERS; - public static float LOW_WEIGHT; - public static float MEDIUM_WEIGHT; - public static float HIGH_WEIGHT; - public static float DIAGONAL_WEIGHT; + public static int MOVE_WEIGHT; + public static int MOVE_WEIGHT_DIAG; + public static int OBSTACLE_WEIGHT; + 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; // -------------------------------------------------- // Custom Settings @@ -2594,12 +2600,17 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); + GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); - MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); - HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); - DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); + MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); + OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); + HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); + MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); + MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); // Load AllowedPlayerRaces config file (if exists) final PropertiesParser AllowedPlayerRaces = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE); diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/commons/util/CommonUtil.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/commons/util/CommonUtil.java index 6f26027539..7aafc74eac 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/commons/util/CommonUtil.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/commons/util/CommonUtil.java @@ -266,4 +266,15 @@ public class CommonUtil } return false; } + + /** + * @param numToTest : The number to test. + * @param min : The minimum limit. + * @param max : The maximum limit. + * @return the number or one of the limit (mininum / maximum). + */ + public static int limit(int numToTest, int min, int max) + { + return (numToTest > max) ? max : ((numToTest < min) ? min : numToTest); + } } diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/commons/util/Point2D.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/commons/util/Point2D.java new file mode 100644 index 0000000000..ebb6272e5e --- /dev/null +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/commons/util/Point2D.java @@ -0,0 +1,180 @@ +/* + * 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 . + */ +package org.l2jmobius.commons.util; + +import java.util.Objects; + +/** + * A datatype used to retain a 2D (x/y) point. It got the capability to be set and cleaned. + */ +public class Point2D +{ + protected volatile int _x; + protected volatile int _y; + + public Point2D(int x, int y) + { + _x = x; + _y = y; + } + + @Override + public Point2D clone() + { + return new Point2D(_x, _y); + } + + @Override + public String toString() + { + return _x + ", " + _y; + } + + @Override + public int hashCode() + { + return Objects.hash(_x, _y); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final Point2D other = (Point2D) obj; + return (_x == other._x) && (_y == other._y); + } + + /** + * @param x : The X coord to test. + * @param y : The Y coord to test. + * @return True if all coordinates equals this {@link Point2D} coordinates. + */ + public boolean equals(int x, int y) + { + return (_x == x) && (_y == y); + } + + public int getX() + { + return _x; + } + + public void setX(int x) + { + _x = x; + } + + public int getY() + { + return _y; + } + + public void setY(int y) + { + _y = y; + } + + public void set(int x, int y) + { + _x = x; + _y = y; + } + + /** + * Refresh the current {@link Point2D} using a reference {@link Point2D} and a distance. The new destination is calculated to go in opposite side of the {@link Point2D} reference.
+ *
+ * This method is perfect to calculate fleeing characters position. + * @param referenceLoc : The Point2D used as position. + * @param distance : The distance to be set between current and new position. + */ + public void setFleeing(Point2D referenceLoc, int distance) + { + final double xDiff = referenceLoc.getX() - _x; + final double yDiff = referenceLoc.getY() - _y; + + final double yxRation = Math.abs(xDiff / yDiff); + + final int y = (int) (distance / (yxRation + 1)); + final int x = (int) (y * yxRation); + + _x += (xDiff < 0 ? x : -x); + _y += (yDiff < 0 ? y : -y); + } + + public void clean() + { + _x = 0; + _y = 0; + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @return The distance between this {@Point2D} and some given coordinates. + */ + public double distance2D(int x, int y) + { + final double dx = (double) _x - x; + final double dy = (double) _y - y; + + return Math.sqrt((dx * dx) + (dy * dy)); + } + + /** + * @param point : The {@link Point2D} to test. + * @return The distance between this {@Point2D} and the {@link Point2D} set as parameter. + */ + public double distance2D(Point2D point) + { + return distance2D(point.getX(), point.getY()); + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of some given coordinates. + */ + public boolean isIn2DRadius(int x, int y, int radius) + { + return distance2D(x, y) < radius; + } + + /** + * @param point : The Point2D to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of the {@link Point2D} set as parameter. + */ + public boolean isIn2DRadius(Point2D point, int radius) + { + return distance2D(point) < radius; + } +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/AttackableAI.java index 765bcef311..dc0b2925b5 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/AttackableAI.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/AttackableAI.java @@ -710,7 +710,7 @@ public class AttackableAI extends CreatureAI } // Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast) - final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceId()); + final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceId()); moveTo(moveLoc.getX(), moveLoc.getY(), moveLoc.getZ()); } } @@ -941,7 +941,7 @@ public class AttackableAI extends CreatureAI final int newZ = npc.getZ() + 30; // Mobius: Verify destination. Prevents wall collision issues and fixes monsters not avoiding obstacles. - moveTo(GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceId())); + moveTo(GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceId())); } return; } diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/CreatureAI.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/CreatureAI.java index 60ef097bfc..5bb0ea4d3a 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/CreatureAI.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/CreatureAI.java @@ -925,7 +925,7 @@ public class CreatureAI extends AbstractAI } // If pathfinding enabled the creature will go to the destination or it will go to the nearest obstacle. - setIntention(AI_INTENTION_MOVE_TO, Config.PATHFINDING ? GeoEngine.getInstance().canMoveToTargetLoc(_actor.getX(), _actor.getY(), _actor.getZ(), posX, posY, posZ, _actor.getInstanceId()) : new Location(posX, posY, posZ)); + setIntention(AI_INTENTION_MOVE_TO, Config.PATHFINDING ? GeoEngine.getInstance().getValidLocation(_actor.getX(), _actor.getY(), _actor.getZ(), posX, posY, posZ, _actor.getInstanceId()) : new Location(posX, posY, posZ)); } protected boolean maybeMoveToPosition(ILocational worldPosition, int offset) diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/SummonAI.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/SummonAI.java index d56be32b4c..217d288f9d 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/SummonAI.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/ai/SummonAI.java @@ -26,7 +26,6 @@ import org.l2jmobius.Config; import org.l2jmobius.commons.concurrent.ThreadPool; import org.l2jmobius.commons.util.Rnd; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; import org.l2jmobius.gameserver.model.WorldObject; import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.actor.Summon; @@ -51,7 +50,7 @@ public class SummonAI extends PlayableAI implements Runnable @Override protected void onIntentionAttack(Creature target) { - if (Config.PATHFINDING && (GeoEnginePathfinding.getInstance().findPath(_actor.getX(), _actor.getY(), _actor.getZ(), target.getX(), target.getY(), target.getZ(), _actor.getInstanceId()) == null)) + if (Config.PATHFINDING && (GeoEngine.getInstance().findPath(_actor.getX(), _actor.getY(), _actor.getZ(), target.getX(), target.getY(), target.getZ(), _actor.getInstanceId()) == null)) { return; } diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/data/SpawnTable.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/data/SpawnTable.java index 49bcc79768..60b1133986 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/data/SpawnTable.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/data/SpawnTable.java @@ -400,8 +400,8 @@ public class SpawnTable implements IXmlReader } // XML file for spawn - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); // Write info to XML @@ -476,8 +476,8 @@ public class SpawnTable implements IXmlReader if (update) { - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final int npcSpawnTemplateId = spawn.getNpcSpawnTemplateId(); final File spawnFile = npcSpawnTemplateId > 0 ? new File(_spawnTemplates.get(npcSpawnTemplateId)) : new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); final File tempFile = new File(spawnFile.getAbsolutePath().substring(Config.DATAPACK_ROOT.getAbsolutePath().length() + 1).replace('\\', '/') + ".tmp"); diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/enums/GeoType.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/enums/GeoType.java new file mode 100644 index 0000000000..f77aa5eac4 --- /dev/null +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/enums/GeoType.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.enums; + +public enum GeoType +{ + L2J("%d_%d.l2j"), + L2OFF("%d_%d_conv.dat"); + + private final String _filename; + + private GeoType(String filename) + { + _filename = filename; + } + + public String getFilename() + { + return _filename; + } +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java new file mode 100644 index 0000000000..e62f50a8df --- /dev/null +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java @@ -0,0 +1,144 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 7b814dd4fd..4679a89eb9 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,71 +16,62 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.channels.FileChannel.MapMode; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.logging.Level; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; import java.util.logging.Logger; import org.l2jmobius.Config; +import org.l2jmobius.commons.util.CommonUtil; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; -import org.l2jmobius.gameserver.geoengine.geodata.IRegion; -import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; -import org.l2jmobius.gameserver.geoengine.geodata.Region; +import org.l2jmobius.gameserver.enums.GeoType; +import org.l2jmobius.gameserver.enums.MoveDirectionType; +import org.l2jmobius.gameserver.geoengine.geodata.ABlock; +import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; +import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; +import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; +import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; -import org.l2jmobius.gameserver.model.interfaces.ILocational; -import org.l2jmobius.gameserver.util.GeoUtils; -import org.l2jmobius.gameserver.util.LinePointIterator; -import org.l2jmobius.gameserver.util.LinePointIterator3D; +import org.l2jmobius.gameserver.model.actor.Creature; -/** - * @author -Nemesiss-, HorridoJoho - */ public class GeoEngine { - private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - - private static final int WORLD_MIN_X = -655360; - private static final int WORLD_MIN_Y = -589824; - private static final int WORLD_MIN_Z = -16384; - - /** 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; - - /** The regions array */ - private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; - private static final int ELEVATED_SEE_OVER_DISTANCE = 2; - private static final int MAX_SEE_OVER_HEIGHT = 48; - protected GeoEngine() + private final ABlock[][] _blocks; + private final BlockNull _nullBlock; + + // Pre-allocated buffers. + private final BufferHolder[] _buffers; + + public GeoEngine() { LOGGER.info("GeoEngine: Initializing..."); - for (int i = 0; i < _regions.length(); i++) - { - _regions.set(i, NullRegion.INSTANCE); - } + // Initialize block container. + _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; + + // Load null block. + _nullBlock = new BlockNull(); + + // Initialize multilayer temporarily buffer. + BlockMultilayer.initialize(); + + // Load geo files according to geoengine config setup. int loaded = 0; try { @@ -91,23 +82,52 @@ public class GeoEngine final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); if (Files.exists(geoFilePath)) { - try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + // Region file is load-able, try to load it. + if (loadGeoBlocks(regionX, regionY)) { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); loaded++; } } + else + { + // Region file is not load-able, load null blocks. + loadNullBlocks(regionX, regionY); + } } } } catch (Exception e) { - LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + LOGGER.warning("GeoEngine: Failed to load geodata! " + e); System.exit(1); } - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + // Release multilayer block temporarily buffer. + BlockMultilayer.release(); + + String[] array = Config.PATHFIND_BUFFERS.split(";"); + _buffers = new BufferHolder[array.length]; + + int count = 0; + for (int i = 0; i < array.length; i++) + { + String buf = array[i]; + String[] args = buf.split("x"); + + try + { + int size = Integer.parseInt(args[1]); + count += size; + _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); + } + catch (Exception e) + { + LOGGER.warning("Could not load buffer setting:" + buf + ". " + e); + } + } + LOGGER.info("Loaded " + count + " node buffers."); + // Avoid wrong configs when no files are loaded. if ((loaded == 0) && Config.PATHFINDING) { @@ -117,616 +137,913 @@ public class GeoEngine } /** - * @param geoX - * @param geoY - * @return the region + * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer and log this situation. + * @param size : pre-calculated minimal required size + * @return NodeBuffer : buffer */ - private IRegion getRegion(int geoX, int geoY) + private NodeBuffer getBuffer(int size) { - final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); - if ((region < 0) || (region >= _regions.length())) + NodeBuffer current = null; + for (BufferHolder holder : _buffers) { - return null; - } - return _regions.get(region); - } - - /** - * @param filePath - * @param regionX - * @param regionY - * @throws IOException - */ - 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()).load().order(ByteOrder.LITTLE_ENDIAN))); - } - } - - /** - * @param regionX - * @param regionY - */ - public void unloadRegion(int regionX, int regionY) - { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); - } - - /** - * @param geoX - * @param geoY - * @return if geodata exist - */ - public boolean hasGeoPos(int geoX, int geoY) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return false; - } - return region.hasGeo(); - } - - /** - * Checks the specified position for available geodata. - * @param x the world x - * @param y the world y - * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise - */ - public boolean hasGeo(int x, int y) - { - return hasGeoPos(getGeoX(x), getGeoY(y)); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe check - */ - public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return true; - } - return region.checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe anti-corner cut - */ - public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) - { - boolean can = true; - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - return can && checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the nearest Z value - */ - public int getNearestZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNearestZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next lower Z value - */ - public int getNextLowerZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextLowerZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next higher Z value - */ - public int getNextHigherZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextHigherZ(geoX, geoY, worldZ); - } - - /** - * Gets the Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHeight(int x, int y, int z) - { - return getNearestZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next lower Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getLowerHeight(int x, int y, int z) - { - return getNextLowerZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next higher Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHigherHeight(int x, int y, int z) - { - return getNextHigherZ(getGeoX(x), getGeoY(y), z); - } - - /** - * @param worldX - * @return the geo X - */ - public int getGeoX(int worldX) - { - return (worldX - WORLD_MIN_X) / 16; - } - - /** - * @param worldY - * @return the geo Y - */ - public int getGeoY(int worldY) - { - return (worldY - WORLD_MIN_Y) / 16; - } - - /** - * @param worldZ - * @return the geo Z - */ - public int getGeoZ(int worldZ) - { - return (worldZ - WORLD_MIN_Z) / 16; - } - - /** - * @param geoX - * @return the world X - */ - public int getWorldX(int geoX) - { - return (geoX * 16) + WORLD_MIN_X + 8; - } - - /** - * @param geoY - * @return the world Y - */ - public int getWorldY(int geoY) - { - return (geoY * 16) + WORLD_MIN_Y + 8; - } - - /** - * @param geoZ - * @return the world Z - */ - public int getWorldZ(int geoZ) - { - return (geoZ * 16) + WORLD_MIN_Z + 8; - } - - /** - * Can see target. Doors as target always return true. Checks doors between. - * @param cha the character - * @param target the target - * @return {@code true} if the character can see the target (LOS), {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, WorldObject target) - { - if (target.isDoor()) - { - // Can always see doors. - return true; - } - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), target.getX(), target.getY(), target.getZ(), target.getInstanceId()); - } - - /** - * Can see target. Checks doors between. - * @param cha the character - * @param worldPosition the world position - * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) - { - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceId(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instanceId - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @param tInstanceId the target's instanceId - * @return - */ - public boolean canSeeTarget(int x, int y, int z, int instanceId, int tx, int ty, int tz, int tInstanceId) - { - return (instanceId != tInstanceId) ? false : canSeeTarget(x, y, z, instanceId, tx, ty, tz); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instanceId - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int x, int y, int z, int instanceId, int tx, int ty, int tz) - { - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instanceId, true)) - { - return false; - } - return canSeeTarget(x, y, z, tx, ty, tz); - } - - /** - * @param prevX - * @param prevY - * @param prevGeoZ - * @param curX - * @param curY - * @param nswe - * @return the LOS Z value - */ - private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) - { - if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) - { - throw new RuntimeException("Multiple directions!"); - } - - if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) - { - return getNearestZ(curX, curY, prevGeoZ); - } - - return getNextHigherZ(curX, curY, prevGeoZ); - } - - /** - * Can see target. Does not check doors between. - * @param xValue the x coordinate - * @param yValue the y coordinate - * @param zValue the z coordinate - * @param txValue the target's x coordinate - * @param tyValue the target's y coordinate - * @param tzValue the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) - { - int x = xValue; - int y = yValue; - int tx = txValue; - int ty = tyValue; - - int geoX = getGeoX(x); - int geoY = getGeoY(y); - int tGeoX = getGeoX(tx); - int tGeoY = getGeoY(ty); - - int z = getNearestZ(geoX, geoY, zValue); - int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - // Fastpath. - if ((geoX == tGeoX) && (geoY == tGeoY)) - { - if (hasGeoPos(tGeoX, tGeoY)) - { - return z == tz; - } - return true; - } - - if (tz > z) - { - int tmp = tx; - tx = x; - x = tmp; - - tmp = ty; - ty = y; - y = tmp; - - tmp = tz; - tz = z; - z = tmp; - - tmp = tGeoX; - tGeoX = geoX; - geoX = tmp; - - tmp = tGeoY; - tGeoY = geoY; - geoY = tmp; - } - - final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); - // First point is guaranteed to be available, skip it, we can always see our own position. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - final int prevZ = pointIter.z(); - int prevGeoZ = prevZ; - int ptIndex = 0; - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - - if ((curX == prevX) && (curY == prevY)) + // Find proper size of buffer. + if (holder._size < size) { continue; } - final int beeCurZ = pointIter.z(); - int curGeoZ = prevGeoZ; - - // Check if the position has geodata. - if (hasGeoPos(curX, curY)) + // Find unlocked NodeBuffer. + for (NodeBuffer buffer : holder._buffer) { - final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); - curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); - int maxHeight; - if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) + if (!buffer.isLocked()) { - maxHeight = z + MAX_SEE_OVER_HEIGHT; - } - else - { - maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; + continue; } - boolean canSeeThrough = false; - if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) + return buffer; + } + + LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); + + // NodeBuffer not found, allocate temporary buffer. + current = new NodeBuffer(holder._size); + current.isLocked(); + } + + return current; + } + + /** + * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + * @return boolean : True, when geodata file was loaded without problem. + */ + private boolean loadGeoBlocks(int regionX, int regionY) + { + final String filename = String.format(Config.GEODATA_TYPE.getFilename(), regionX, regionY); + final String filepath = Config.GEODATA_PATH + "\\" + filename; + + // Standard load. + try (RandomAccessFile raf = new RandomAccessFile(filepath, "r"); + FileChannel fc = raf.getChannel()) + { + // Initialize file buffer. + MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // Load 18B header for L2OFF geodata (1st and 2nd byte...region X and Y). + if (Config.GEODATA_TYPE == GeoType.L2OFF) + { + for (int i = 0; i < 18; i++) { - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) + buffer.get(); + } + } + + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Loop over region blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + if (Config.GEODATA_TYPE == GeoType.L2J) { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) - { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); + // Get block type. + final byte type = buffer.get(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + case GeoStructure.TYPE_MULTILAYER_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + default: + { + throw new IllegalArgumentException("Unknown block type: " + type); + } + } } else { - canSeeThrough = true; + // Get block type. + final short type = buffer.getShort(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + default: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + } } } + } + + // Check data consistency. + if (buffer.remaining() > 0) + { + LOGGER.warning("Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); + } + + // Loading was successful. + return true; + } + catch (Exception e) + { + // An error occured while loading, load null blocks. + LOGGER.warning("Error loading " + filename + " region file. " + e); + + // Replace whole region file with null blocks. + loadNullBlocks(regionX, regionY); + + // Loading was not successful. + return false; + } + } + + /** + * Loads null blocks. Used when no region file is detected or an error occurs during loading. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + */ + private void loadNullBlocks(int regionX, int regionY) + { + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Load all null blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + _blocks[blockX + ix][blockY + iy] = _nullBlock; + } + } + } + + /** + * Converts world X to geodata X. + * @param worldX + * @return int : Geo X + */ + public static int getGeoX(int worldX) + { + return (worldX - World.WORLD_X_MIN) >> 4; + } + + /** + * Converts world Y to geodata Y. + * @param worldY + * @return int : Geo Y + */ + public static int getGeoY(int worldY) + { + return (worldY - World.WORLD_Y_MIN) >> 4; + } + + /** + * Converts geodata X to world X. + * @param geoX + * @return int : World X + */ + public static int getWorldX(int geoX) + { + return (geoX << 4) + World.WORLD_X_MIN + 8; + } + + /** + * Converts geodata Y to world Y. + * @param geoY + * @return int : World Y + */ + public static int getWorldY(int geoY) + { + return (geoY << 4) + World.WORLD_Y_MIN + 8; + } + + /** + * Returns block of geodata on given coordinates. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return {@link ABlock} : Block of geodata. + */ + public ABlock getBlock(int geoX, int geoY) + { + final int x = geoX / GeoStructure.BLOCK_CELLS_X; + if ((x < 0) || (x >= GeoStructure.GEO_BLOCKS_X)) + { + return null; + } + final int y = geoY / GeoStructure.BLOCK_CELLS_Y; + if ((y < 0) || (y >= GeoStructure.GEO_BLOCKS_Y)) + { + return null; + } + return _blocks[x][y]; + } + + /** + * Check if geo coordinates has geo. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return boolean : True, if given geo coordinates have geodata + */ + public boolean hasGeoPos(int geoX, int geoY) + { + final ABlock block = getBlock(geoX, geoY); + return (block != null) && block.hasGeoPos(); + } + + /** + * Returns the height of cell, which is closest to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell geodata Z coordinate, closest to given coordinates. + */ + public short getHeightNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return (short) worldZ; + } + return block.getHeightNearest(geoX, geoY, worldZ); + } + + /** + * Returns the NSWE flag byte of cell, which is closes to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. + */ + public byte getNsweNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return GeoStructure.CELL_FLAG_ALL; + } + return block.getNsweNearest(geoX, geoY, worldZ); + } + + /** + * Check if world coordinates has geo. + * @param worldX : World X + * @param worldY : World Y + * @return boolean : True, if given world coordinates have geodata + */ + public boolean hasGeo(int worldX, int worldY) + { + return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param loc : The location used as reference. + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(Location loc) + { + return getHeightNearest(getGeoX(loc.getX()), getGeoY(loc.getY()), loc.getZ()); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param worldX : world x + * @param worldY : world y + * @param worldZ : world z + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(int worldX, int worldY, int worldZ) + { + return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); + } + + /** + * Check line of sight from {@link WorldObject} to {@link WorldObject}.
+ * @param object : The origin object. + * @param target : The target object. + * @return True, when object can see target. + */ + public boolean canSeeTarget(WorldObject object, WorldObject target) + { + // Can always see doors. + if (target.isDoor()) + { + return true; + } + + if (object.getInstanceId() != target.getInstanceId()) + { + return false; + } + + if (DoorData.getInstance().checkIfDoorsBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceId(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceId())) + { + return false; + } + + // Get object's and target's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getTemplate().getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + double theight = 0; + if (target instanceof Creature) + { + theight += (((Creature) target).getTemplate().getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + return canSee(object.getX(), object.getY(), object.getZ(), oheight, target.getX(), target.getY(), target.getZ(), theight) && canSee(target.getX(), target.getY(), target.getZ(), theight, object.getX(), object.getY(), object.getZ(), oheight); + } + + /** + * Check line of sight from {@link WorldObject} to {@link Location}.
+ * Note: The check uses {@link Location}'s real Z coordinate (e.g. point above ground), not its geodata representation. + * @param object : The origin object. + * @param position : The target position. + * @return True, when object can see position. + */ + public boolean canSeeLocation(WorldObject object, Location position) + { + // Get object and location coordinates. + int ox = object.getX(); + int oy = object.getY(); + int oz = object.getZ(); + int tx = position.getX(); + int ty = position.getY(); + int tz = position.getZ(); + + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, object.getInstanceId(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, object.getInstanceId())) + { + return false; + } + + // Get object's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getTemplate().getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + // Perform geodata check. + return canSee(ox, oy, oz, oheight, tx, ty, tz, 0) && canSee(tx, ty, tz, 0, ox, oy, oz, oheight); + } + + /** + * Simple check for origin to target visibility.
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param oheight : The height of origin, used as start point. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param theight : The height of target, used as end point. + * @return True, when origin can see target. + */ + public boolean canSee(int ox, int oy, int oz, double oheight, int tx, int ty, int tz, double theight) + { + // Get origin geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get target geodata coordinates. + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check being on same cell and layer (index). + // Note: Get index must use origin height increased by cell height, the method returns index to height exclusive self. + int index = block.getIndexBelow(gox, goy, oz + GeoStructure.CELL_HEIGHT); + if (index < 0) + { + return false; + } + + if ((gox == gtx) && (goy == gty)) + { + return index == block.getIndexBelow(gtx, gty, tz + GeoStructure.CELL_HEIGHT); + } + + // Get ground and nswe flag. + int groundZ = block.getHeight(index); + int nswe = block.getNswe(index); + + // Get delta coordinates, slope of line (XY, XZ) and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double dz = (tz + theight) - (oz + oheight); + final double m = (double) dy / dx; + final double mz = dz / Math.sqrt((dx * dx) + (dy * dy)); + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); + + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) + { + // Set next cell in X direction. + gridX += mdt.getStepX(); + gox += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); - if (!canSeeThrough) - { - return false; - } + // Set next cell in Y direction. + gridY += mdt.getStepY(); + goy += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevGeoZ = curGeoZ; - ++ptIndex; + // Get block of the next cell. + block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get line of sight height (including Z slope). + double losz = oz + oheight + Config.MAX_OBSTACLE_HEIGHT; + losz += mz * Math.sqrt(((checkX - ox) * (checkX - ox)) + ((checkY - oy) * (checkY - oy))); + + // Check line of sight going though wall (vertical check). + + // Get index of particular layer, based on last iterated cell conditions. + boolean canMove = (nswe & dir) != 0; + if (canMove) + { + // No wall present, get next cell below current cell. + index = block.getIndexBelow(gox, goy, groundZ + GeoStructure.CELL_IGNORE_HEIGHT); + } + else + { + // Wall present, get next cell above current cell. + index = block.getIndexAbove(gox, goy, groundZ - (2 * GeoStructure.CELL_HEIGHT)); + } + + // Next cell's does not exist (no geodata with valid condition), return fail. + if (index < 0) + { + return false; + } + + // Get next cell's layer height. + int z = block.getHeight(index); + + // Perform sine of sight check (next cell is above line of sight line), return fail. + if (z > losz) + { + return false; + } + + // Next cell is accessible, update z and NSWE. + groundZ = z; + nswe = block.getNswe(index); } + // Iteration is completed, no obstacle is found. return true; } /** - * Move check. - * @param x the x coordinate - * @param y the y coordinate - * @param zValue the z coordinate - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tzValue the target's z coordinate - * @param instanceId the instance id - * @return the last Location (x,y,z) where player can walk - just before wall + * Check movement from coordinates to coordinates.
+ * Note: The Z coordinates are supposed to be already validated geodata coordinates. + * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instanceId + * @return True, when target coordinates are reachable from origin coordinates. */ - public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, int instanceId) + public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) { - final int geoX = getGeoX(x); - final int geoY = getGeoY(y); - final int z = getNearestZ(geoX, geoY, zValue); - final int tGeoX = getGeoX(tx); - final int tGeoY = getGeoY(ty); - final int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instanceId, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instanceId, false)) { - return new Location(x, y, getHeight(x, y, z)); - } - if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instanceId)) - { - return new Location(x, y, getHeight(x, y, z)); + return false; } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = z; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instanceId)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return false; + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + int goz = getHeightNearest(gox, goy, oz); + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check movement within same cell. + if ((gox == gtx) && (goy == gty)) + { + return goz == getHeight(tx, ty, tz); + } + + // Get nswe flag. + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - // Can't move, return previous location. - return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check point heading into obstacle, if so return current point. + if ((nswe & dir) == 0) + { + return false; + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return false; + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != tz)) - { - // Different floors, return start location. - return new Location(x, y, z); - } - - return new Location(tx, ty, tz); + // When origin Z is target Z, the move is successful. + return goz == getHeight(tx, ty, tz); } /** - * Checks if its possible to move from one location to another. - * @param fromX the X coordinate to start checking from - * @param fromY the Y coordinate to start checking from - * @param fromZvalue the Z coordinate to start checking from - * @param toX the X coordinate to end checking at - * @param toY the Y coordinate to end checking at - * @param toZvalue the Z coordinate to end checking at - * @param instanceId the instance - * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + * Check movement from origin to target coordinates. Returns last available point in the checked path.
+ * Target X and Y reachable and Z is on same floor: + *
    + *
  • Location of the target with corrected Z value from geodata.
  • + *
+ * Target X and Y reachable but Z is on another floor: + *
    + *
  • Location of the origin with corrected Z value from geodata.
  • + *
+ * Target X and Y not reachable: + *
    + *
  • Last accessible location in destination to target.
  • + *
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instanceId + * @return The {@link Location} representing last point of movement (e.g. just before wall). */ - public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, int instanceId) + public Location getValidLocation(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) { - final int geoX = getGeoX(fromX); - final int geoY = getGeoY(fromY); - final int fromZ = getNearestZ(geoX, geoY, fromZvalue); - final int tGeoX = getGeoX(toX); - final int tGeoY = getGeoY(toY); - final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); - - if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instanceId, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instanceId, false)) { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instanceId)) - { - return false; + return new Location(ox, oy, oz); } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = fromZ; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instanceId)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return new Location(ox, oy, oz); + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + final int gtz = getHeightNearest(gtx, gty, tz); + int goz = getHeightNearest(gox, goy, oz); + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - return false; - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check target cell is out of geodata grid (world coordinates). + if ((nx < 0) || (nx >= GeoStructure.GEO_CELLS_X) || (ny < 0) || (ny >= GeoStructure.GEO_CELLS_Y)) + { + return new Location(checkX, checkY, goz); + } + + // Check point heading into obstacle, if so return current (border) point. + if ((nswe & dir) == 0) + { + return new Location(checkX, checkY, goz); + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current (border) point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return new Location(checkX, checkY, goz); + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) - { - // Different floors. - return false; - } - - return true; + // Compare Z coordinates: + // If same, path is okay, return target point and fix its Z geodata coordinate. + // If not same, path is does not exist, return origin point. + return goz == gtz ? new Location(tx, ty, gtz) : new Location(ox, oy, oz); } + /** + * Returns the list of location objects as a result of complete path calculation. + * @param ox : origin x + * @param oy : origin y + * @param oz : origin z + * @param tx : target x + * @param ty : target y + * @param tz : target z + * @param instanceId + * @return {@code List} : complete path from nodes + */ + public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, int instanceId) + { + // Get origin and check existing geo coords. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + if (!hasGeoPos(gox, goy)) + { + return Collections.emptyList(); + } + + int goz = getHeightNearest(gox, goy, oz); + + // Get target and check existing geo coords. + int gtx = getGeoX(tx); + int gty = getGeoY(ty); + if (!hasGeoPos(gtx, gty)) + { + return Collections.emptyList(); + } + + int gtz = getHeightNearest(gtx, gty, tz); + + // Prepare buffer for pathfinding calculations. + NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + if (buffer == null) + { + return Collections.emptyList(); + } + + // Find path. + List path = null; + try + { + path = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + if (path.isEmpty()) + { + return Collections.emptyList(); + } + } + catch (Exception e) + { + return Collections.emptyList(); + } + finally + { + buffer.free(); + } + + // Check path. + if (path.size() < 3) + { + return path; + } + + // Get path list iterator. + ListIterator point = path.listIterator(); + + // Get node A (origin). + int nodeAx = ox; + int nodeAy = oy; + int nodeAz = goz; + + // Get node B. + Location nodeB = point.next(); + + // Iterate thought the path to optimize it. + while (point.hasNext()) + { + // Get node C. + Location nodeC = path.get(point.nextIndex()); + + // Check movement from node A to node C. + if (canMoveToTarget(nodeAx, nodeAy, nodeAz, nodeC.getX(), nodeC.getY(), nodeC.getZ(), instanceId)) + { + // Can move from node A to node C. + // Remove node B. + point.remove(); + } + else + { + // Can not move from node A to node C. + // Set node A (node B is part of path, update A coordinates). + nodeAx = nodeB.getX(); + nodeAy = nodeB.getY(); + nodeAz = nodeB.getZ(); + } + + // Set node B. + nodeB = point.next(); + } + + return path; + } + + /** + * NodeBuffer container with specified size and count of separate buffers. + */ + private static class BufferHolder + { + final int _size; + final List _buffer; + + public BufferHolder(int size, int count) + { + _size = size; + _buffer = new ArrayList<>(count); + + for (int i = 0; i < count; i++) + { + _buffer.add(new NodeBuffer(size)); + } + } + } + + /** + * Returns the instance of the {@link GeoEngine}. + * @return {@link GeoEngine} : The instance. + */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -736,4 +1053,4 @@ public class GeoEngine { protected static final GeoEngine INSTANCE = new GeoEngine(); } -} +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java deleted file mode 100644 index 43ac4d89b8..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ /dev/null @@ -1,300 +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 . - */ -package org.l2jmobius.gameserver.geoengine; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; -import org.l2jmobius.gameserver.model.World; - -/** - * @author -Nemesiss- - */ -public class GeoEnginePathfinding -{ - private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); - - private BufferInfo[] _buffers; - - protected GeoEnginePathfinding() - { - try - { - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - - _buffers = 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); - } - - _buffers[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"); - } - } - - public boolean pathNodesExist(short regionoffset) - { - return false; - } - - public List findPath(int x, int y, int z, int tx, int ty, int tz, int instanceId) - { - 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)))); - if (buffer == null) - { - return null; - } - - List path = null; - try - { - final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); - - if (result == null) - { - return null; - } - - path = constructPath(result); - } - catch (Exception e) - { - LOGGER.warning(e.getMessage()); - return null; - } - finally - { - buffer.free(); - } - - // check path - if (path.size() < 3) - { - return path; - } - - int currentX, currentY, currentZ; - ListIterator middlePoint; - - middlePoint = path.listIterator(); - currentX = x; - currentY = y; - currentZ = z; - - while (middlePoint.hasNext()) - { - final AbstractNodeLoc locMiddle = middlePoint.next(); - if (!middlePoint.hasNext()) - { - break; - } - - final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); - if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instanceId)) - { - middlePoint.remove(); - } - else - { - currentX = locMiddle.getX(); - currentY = locMiddle.getY(); - currentZ = locMiddle.getZ(); - } - } - - return path; - } - - /** - * Convert geodata position to pathnode position - * @param geo_pos - * @return pathnode position - */ - public short getNodePos(int geo_pos) - { - return (short) (geo_pos >> 3); // OK? - } - - /** - * Convert node position to pathnode block position - * @param node_pos - * @return pathnode block position (0...255) - */ - public short getNodeBlock(int node_pos) - { - return (short) (node_pos % 256); - } - - public byte getRegionX(int node_pos) - { - return (byte) ((node_pos >> 8) + World.TILE_X_MIN); - } - - public byte getRegionY(int node_pos) - { - return (byte) ((node_pos >> 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 node_x rx - * @return - */ - public int calculateWorldX(short node_x) - { - return World.MAP_MIN_X + (node_x * 128) + 48; - } - - /** - * Convert pathnode y to World y position - * @param node_y - * @return - */ - public int calculateWorldY(short node_y) - { - return World.MAP_MIN_Y + (node_y * 128) + 48; - } - - private List constructPath(AbstractNode nodeValue) - { - final LinkedList path = new LinkedList<>(); - int previousDirectionX = Integer.MIN_VALUE; - int previousDirectionY = Integer.MIN_VALUE; - int directionX, directionY; - - AbstractNode node = nodeValue; - while (node.getParent() != null) - { - directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); - directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); - - // only add a new route point if moving direction changes - if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) - { - previousDirectionX = directionX; - previousDirectionY = directionY; - - path.addFirst(node.getLoc()); - node.setLoc(null); - } - - node = node.getParent(); - } - - return path; - } - - private CellNodeBuffer alloc(int size) - { - CellNodeBuffer current = null; - for (BufferInfo i : _buffers) - { - if (i.mapSize >= size) - { - for (CellNodeBuffer buf : i.buffer) - { - if (buf.lock()) - { - current = buf; - break; - } - } - if (current != null) - { - break; - } - - // not found, allocate temporary buffer - current = new CellNodeBuffer(i.mapSize); - current.lock(); - if (i.buffer.size() < i.count) - { - i.buffer.add(current); - break; - } - } - } - - return current; - } - - private static final class BufferInfo - { - final int mapSize; - final int count; - ArrayList buffer; - - public BufferInfo(int size, int cnt) - { - mapSize = size; - count = cnt; - buffer = new ArrayList<>(count); - } - } - - public static GeoEnginePathfinding getInstance() - { - return SingletonHolder.INSTANCE; - } - - private static class SingletonHolder - { - protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); - } -} diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java new file mode 100644 index 0000000000..49ec35aa1c --- /dev/null +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java @@ -0,0 +1,85 @@ +/* + * 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 . + */ +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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @param index : Index of the cell. + * @return short : Cell geodata Z coordinate, below given coordinates. + */ + public abstract byte getNswe(int index); +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java new file mode 100644 index 0000000000..f5997bf287 --- /dev/null +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java @@ -0,0 +1,130 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java new file mode 100644 index 0000000000..9af9d2a28a --- /dev/null +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java @@ -0,0 +1,95 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java new file mode 100644 index 0000000000..31fbf5d477 --- /dev/null +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java new file mode 100644 index 0000000000..f77d21d84b --- /dev/null +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java @@ -0,0 +1,68 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +public class BlockNull extends ABlock +{ + @Override + public boolean hasGeoPos() + { + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java deleted file mode 100644 index 61ea4fcf82..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java +++ /dev/null @@ -1,48 +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 . - */ -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() - { - } -} diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java deleted file mode 100644 index 3c8de1a7f7..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java +++ /dev/null @@ -1,77 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java deleted file mode 100644 index 1ccdefe3da..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java +++ /dev/null @@ -1,56 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java new file mode 100644 index 0000000000..691b164e0c --- /dev/null +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ +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_ALL = 0x0F; + + // 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; +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java deleted file mode 100644 index 25eafc5a04..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java +++ /dev/null @@ -1,42 +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 . - */ -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); -} diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java deleted file mode 100644 index 0d2c765002..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java +++ /dev/null @@ -1,47 +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 . - */ -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(); -} diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java deleted file mode 100644 index 52df457360..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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); - - // 1 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; - } -} diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java deleted file mode 100644 index 72a5a635c7..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java +++ /dev/null @@ -1,55 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author HorridoJoho - */ -public final class NullRegion implements IRegion -{ - public static final NullRegion INSTANCE = new NullRegion(); - - @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 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; - } -} diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java deleted file mode 100644 index fc924cb875..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java +++ /dev/null @@ -1,92 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java deleted file mode 100644 index 5087513ed9..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java +++ /dev/null @@ -1,87 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public abstract class AbstractNode -{ - private T _loc; - private AbstractNode _parent; - - public AbstractNode(T loc) - { - _loc = loc; - } - - public void setParent(AbstractNode p) - { - _parent = p; - } - - public AbstractNode getParent() - { - return _parent; - } - - public T getLoc() - { - return _loc; - } - - public void setLoc(T l) - { - _loc = l; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); - return result; - } - - @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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java deleted file mode 100644 index 7223b5b2be..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java +++ /dev/null @@ -1,33 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -/** - * @author -Nemesiss- - */ -public abstract class AbstractNodeLoc -{ - public abstract int getX(); - - public abstract int getY(); - - public abstract int getZ(); - - public abstract int getNodeX(); - - public abstract int getNodeY(); -} diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java deleted file mode 100644 index 3425234e0e..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public class CellNode extends AbstractNode -{ - 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java deleted file mode 100644 index 522610bb7c..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java +++ /dev/null @@ -1,348 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -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 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) - { - _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(); - } - - public final List debugPath() - { - final List 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 void getNeighbors() - { - if (!_current.getLoc().canGoAll()) - { - 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); - } - - // SouthEast - if ((nodeE != null) && (nodeS != null)) - { - if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) - { - addNode(x + 1, y + 1, z, true); - } - } - - // SouthWest - if ((nodeS != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) - { - addNode(x - 1, y + 1, z, true); - } - } - - // NorthEast - if ((nodeN != null) && (nodeE != null)) - { - if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) - { - addNode(x + 1, y - 1, z, true); - } - } - - // NorthWest - if ((nodeN != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) - { - addNode(x - 1, y - 1, z, true); - } - } - } - - private 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(); - // reinit node if needed - if (result.getLoc() != null) - { - result.getLoc().set(x, y, z); - } - else - { - result.setLoc(new NodeLoc(x, y, z)); - } - } - - return result; - } - - private 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 boolean isHighWeight(int x, int y, int z) - { - final CellNode result = getNode(x, y, z); - if (result == null) - { - return true; - } - - if (!result.getLoc().canGoAll()) - { - return true; - } - if (Math.abs(result.getLoc().getZ() - z) > 16) - { - return true; - } - - return false; - } - - private 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java new file mode 100644 index 0000000000..fa5ca43c2f --- /dev/null +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -0,0 +1,111 @@ +/* + * 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 . + */ +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 geodata values. + private int _geoX; + private int _geoY; + private byte _nswe; + + // 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; + + _costG = 0; + _costH = 0; + _costF = 0; + + _parent = null; + } + + public void setGeo(int gx, int gy, int gz, byte nswe) + { + super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); + + _geoX = gx; + _geoY = gy; + _nswe = nswe; + } + + 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 int getCostF() + { + return _costF; + } + + public Node getParent() + { + return _parent; + } + + @Override + public int compareTo(Node o) + { + return _costF - o._costF; + } +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java new file mode 100644 index 0000000000..b464212c96 --- /dev/null +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -0,0 +1,368 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.awt.Color; +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; +import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; + +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 _opened; + // Container holding Nodes already explored. + private final List _closed; + + // Target coordinates. + private int _gtx; + private int _gty; + private int _gtz; + + // Pathfinding statistics. + private long _timeStamp; + private long _lastElapsedTime; + + 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 findPath(int gox, int goy, int goz, int gtx, int gty, int gtz) + { + // Set start timestamp. + _timeStamp = System.currentTimeMillis(); + + // 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)); + _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 constructPath() + { + // Create result. + final LinkedList 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; + } + + /** + * Creates list of Nodes to show debug path. + * @param debug : The debug packet to add debug informations in. + */ + public void debugPath(ExServerPrimitive debug) + { + // Add all opened node as yellow points. + for (Node n : _opened) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); + } + + // Add all opened node as blue points. + for (Node n : _closed) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); + } + } + + 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; + + _lastElapsedTime = System.currentTimeMillis() - _timeStamp; + _lock.unlock(); + } + + public long getElapsedTime() + { + return _lastElapsedTime; + } + + /** + * Expand the current {@link Node} by exploring its neighbors (axially and diagonally). + */ + private void expand() + { + // Movement is blocked, skip. + final byte nswe = _current.getNSWE(); + if (nswe == 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; + + // Can move north, expand. + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + } + + // Can move south, expand. + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + } + + // Can move west, expand. + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move east, expand. + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move north-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move north-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + } + + /** + * 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 weight : The weight of movement to the new node. + * @return The nswe of the added node. Blank, if not added. + */ + private byte addNode(int gx, int gy, int gzValue, int weight) + { + // 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]; + + // Set node geodata coordinates. + node.setGeo(gx, gy, gz, nswe); + + // 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, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + _opened.add(node); + _bufferIndex++; + return nswe; + } + + /** + * Calculate cost H value, calculated using diagonal distance method.
+ * 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. + final int dx = Math.abs(gx - _gtx); + final int dy = Math.abs(gy - _gty); + final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + + // Get diagonal and axial differences to the target. + final int dd = Math.min(dx, dy); + final int da = Math.max(dx, dy) - dd; + + // Calculate the diagonal distance of the node to the target. + return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + } +} \ No newline at end of file diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java deleted file mode 100644 index e3bad04b25..0000000000 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; - -/** - * @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 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 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; - // return super.hashCode(); - } - - @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; - if (_x != other._x) - { - return false; - } - if (_y != other._y) - { - return false; - } - if (_goNorth != other._goNorth) - { - return false; - } - if (_goEast != other._goEast) - { - return false; - } - if (_goSouth != other._goSouth) - { - return false; - } - if (_goWest != other._goWest) - { - return false; - } - if (_geoHeight != other._geoHeight) - { - return false; - } - return true; - } -} diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java index 28a267c40b..50c3a499ca 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java @@ -91,15 +91,15 @@ public class ZoneManager implements IXmlReader private static final Map SETTINGS = new HashMap<>(); private static final int SHIFT_BY = 15; - private static final int OFFSET_X = Math.abs(World.MAP_MIN_X >> SHIFT_BY); - private static final int OFFSET_Y = Math.abs(World.MAP_MIN_Y >> SHIFT_BY); + private static final int OFFSET_X = Math.abs(World.WORLD_X_MIN >> SHIFT_BY); + private static final int OFFSET_Y = Math.abs(World.WORLD_Y_MIN >> SHIFT_BY); private final Map, ConcurrentHashMap> _classZones = new ConcurrentHashMap<>(); private final Map _spawnTerritories = new ConcurrentHashMap<>(); private final AtomicInteger _lastDynamicId = new AtomicInteger(300000); private List _debugItems; - private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.MAP_MAX_X >> SHIFT_BY) + OFFSET_X + 1][(World.MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y + 1]; + private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.WORLD_X_MAX >> SHIFT_BY) + OFFSET_X + 1][(World.WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y + 1]; /** * Instantiates a new zone manager. @@ -178,7 +178,6 @@ public class ZoneManager implements IXmlReader String zoneType; String zoneShape; final List rs = new ArrayList<>(); - for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling()) { if ("list".equalsIgnoreCase(n.getNodeName())) @@ -195,7 +194,6 @@ public class ZoneManager implements IXmlReader if ("zone".equalsIgnoreCase(d.getNodeName())) { attrs = d.getAttributes(); - attribute = attrs.getNamedItem("type"); if (attribute != null) { @@ -244,7 +242,6 @@ public class ZoneManager implements IXmlReader minZ = parseInteger(attrs, "minZ"); maxZ = parseInteger(attrs, "maxZ"); - zoneType = parseString(attrs, "type"); zoneShape = parseString(attrs, "shape"); @@ -366,7 +363,6 @@ public class ZoneManager implements IXmlReader attrs = cd.getAttributes(); final String name = attrs.getNamedItem("name").getNodeValue(); final String val = attrs.getNamedItem("val").getNodeValue(); - temp.setParameter(name, val); } else if ("spawn".equalsIgnoreCase(cd.getNodeName()) && (temp instanceof ZoneRespawn)) @@ -383,7 +379,6 @@ public class ZoneManager implements IXmlReader attrs = cd.getAttributes(); final String race = attrs.getNamedItem("name").getNodeValue(); final String point = attrs.getNamedItem("point").getNodeValue(); - ((RespawnZone) temp).addRaceRespawnPoint(race, point); } } @@ -410,7 +405,6 @@ public class ZoneManager implements IXmlReader final int bx = ((x + 1) - OFFSET_X) << SHIFT_BY; final int ay = (y - OFFSET_Y) << SHIFT_BY; final int by = ((y + 1) - OFFSET_Y) << SHIFT_BY; - if (temp.getZone().intersectsRectangle(ax, bx, ay, by)) { _zoneRegions[x][y].getZones().put(temp.getId(), temp); @@ -463,7 +457,7 @@ public class ZoneManager implements IXmlReader LOGGER.info(getClass().getSimpleName() + ": Loaded " + _classZones.size() + " zone classes and " + getSize() + " zones."); LOGGER.info(getClass().getSimpleName() + ": Loaded " + _spawnTerritories.size() + " NPC spawn territoriers."); final OptionalInt maxId = _classZones.values().stream().flatMap(map -> map.keySet().stream()).mapToInt(Integer.class::cast).filter(value -> value < 300000).max(); - LOGGER.info(getClass().getSimpleName() + ": Last static id: " + maxId.getAsInt()); + LOGGER.info(getClass().getSimpleName() + ": Last static id " + maxId.getAsInt() + "."); } /** diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/Location.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/Location.java index 480e95582d..c413725eef 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/Location.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/Location.java @@ -16,30 +16,35 @@ */ package org.l2jmobius.gameserver.model; +import java.util.Objects; + +import org.l2jmobius.commons.util.Point2D; import org.l2jmobius.gameserver.model.interfaces.ILocational; import org.l2jmobius.gameserver.model.interfaces.IPositionable; /** - * Location data transfer object.
- * Contains coordinates data, heading and instance Id. - * @author Zoey76 + * A datatype used to retain a 3D (x/y/z/heading/instanceId) point. It got the capability to be set and cleaned. */ -public class Location implements IPositionable +public class Location extends Point2D implements IPositionable { - protected int _x; - protected int _y; - protected int _z; - private int _heading; - private int _instanceId; + protected volatile int _z; + protected volatile int _heading; + protected volatile int _instanceId; public Location(int x, int y, int z) { - this(x, y, z, 0, 0); + super(x, y); + _z = z; + _heading = 0; + _instanceId = 0; } public Location(int x, int y, int z, int heading) { - this(x, y, z, heading, 0); + super(x, y); + _z = z; + _heading = heading; + _instanceId = 0; } public Location(WorldObject obj) @@ -49,13 +54,20 @@ public class Location implements IPositionable public Location(int x, int y, int z, int heading, int instanceId) { - _x = x; - _y = y; + super(x, y); _z = z; _heading = heading; _instanceId = instanceId; } + public Location(StatSet set) + { + super(set.getInt("x", 0), set.getInt("y", 0)); + _z = set.getInt("z", 0); + _heading = set.getInt("heading", 0); + _instanceId = set.getInt("instanceId", 0); + } + /** * Get the x coordinate. * @return the x coordinate @@ -166,15 +178,35 @@ public class Location implements IPositionable _instanceId = loc.getInstanceId(); } + @Override + public void clean() + { + super.clean(); + _z = 0; + _instanceId = 0; + } + + @Override + public Location clone() + { + return new Location(_x, _y, _z); + } + + @Override + public int hashCode() + { + return (31 * super.hashCode()) + Objects.hash(_z); + } + @Override public boolean equals(Object obj) { - if (!(obj instanceof Location)) + if (obj instanceof Location) { - return false; + final Location loc = (Location) obj; + return (getX() == loc.getX()) && (getY() == loc.getY()) && (getZ() == loc.getZ()) && (getHeading() == loc.getHeading()) && (getInstanceId() == loc.getInstanceId()); } - final Location loc = (Location) obj; - return (getX() == loc.getX()) && (getY() == loc.getY()) && (getZ() == loc.getZ()) && (getHeading() == loc.getHeading()) && (getInstanceId() == loc.getInstanceId()); + return false; } @Override diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/World.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/World.java index fcecd966b1..0e0a7f5793 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/World.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/World.java @@ -65,19 +65,19 @@ public class World public static final int TILE_Y_MAX = 26; public static final int TILE_ZERO_COORD_X = 20; public static final int TILE_ZERO_COORD_Y = 18; - public static final int MAP_MIN_X = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; - public static final int MAP_MIN_Y = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; + public static final int WORLD_X_MIN = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; + public static final int WORLD_Y_MIN = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; - public static final int MAP_MAX_X = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; - public static final int MAP_MAX_Y = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; + public static final int WORLD_X_MAX = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; + public static final int WORLD_Y_MAX = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; /** Calculated offset used so top left region is 0,0 */ - public static final int OFFSET_X = Math.abs(MAP_MIN_X >> SHIFT_BY); - public static final int OFFSET_Y = Math.abs(MAP_MIN_Y >> SHIFT_BY); + public static final int OFFSET_X = Math.abs(WORLD_X_MIN >> SHIFT_BY); + public static final int OFFSET_Y = Math.abs(WORLD_Y_MIN >> SHIFT_BY); /** Number of regions. */ - private static final int REGIONS_X = (MAP_MAX_X >> SHIFT_BY) + OFFSET_X; - private static final int REGIONS_Y = (MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y; + private static final int REGIONS_X = (WORLD_X_MAX >> SHIFT_BY) + OFFSET_X; + private static final int REGIONS_Y = (WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y; /** Map containing all the players in game. */ private static final Map _allPlayers = new ConcurrentHashMap<>(); @@ -783,9 +783,6 @@ public class World } } - /** - * @return the current instance of World - */ public static World getInstance() { return SingletonHolder.INSTANCE; diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/WorldObject.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/WorldObject.java index f85f231ede..a86fa382d6 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/WorldObject.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/WorldObject.java @@ -191,23 +191,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif synchronized (this) { int spawnX = x; - if (spawnX > World.MAP_MAX_X) + if (spawnX > World.WORLD_X_MAX) { - spawnX = World.MAP_MAX_X - 5000; + spawnX = World.WORLD_X_MAX - 5000; } - if (spawnX < World.MAP_MIN_X) + if (spawnX < World.WORLD_X_MIN) { - spawnX = World.MAP_MIN_X + 5000; + spawnX = World.WORLD_X_MIN + 5000; } int spawnY = y; - if (spawnY > World.MAP_MAX_Y) + if (spawnY > World.WORLD_Y_MAX) { - spawnY = World.MAP_MAX_Y - 5000; + spawnY = World.WORLD_Y_MAX - 5000; } - if (spawnY < World.MAP_MIN_Y) + if (spawnY < World.WORLD_Y_MIN) { - spawnY = World.MAP_MIN_Y + 5000; + spawnY = World.WORLD_Y_MIN + 5000; } // Set the x,y,z position of the WorldObject. If flagged with _isSpawned, setXYZ will automatically update world region, so avoid that. @@ -529,23 +529,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif public void setXYZInvisible(int x, int y, int z) { int correctX = x; - if (correctX > World.MAP_MAX_X) + if (correctX > World.WORLD_X_MAX) { - correctX = World.MAP_MAX_X - 5000; + correctX = World.WORLD_X_MAX - 5000; } - if (correctX < World.MAP_MIN_X) + if (correctX < World.WORLD_X_MIN) { - correctX = World.MAP_MIN_X + 5000; + correctX = World.WORLD_X_MIN + 5000; } int correctY = y; - if (correctY > World.MAP_MAX_Y) + if (correctY > World.WORLD_Y_MAX) { - correctY = World.MAP_MAX_Y - 5000; + correctY = World.WORLD_Y_MAX - 5000; } - if (correctY < World.MAP_MIN_Y) + if (correctY < World.WORLD_Y_MIN) { - correctY = World.MAP_MIN_Y + 5000; + correctY = World.WORLD_Y_MIN + 5000; } setXYZ(correctX, correctY, z); diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Creature.java index 9a51680c04..598d20bcad 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -57,8 +57,6 @@ import org.l2jmobius.gameserver.enums.ShotType; import org.l2jmobius.gameserver.enums.Team; import org.l2jmobius.gameserver.enums.TeleportWhereType; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.InstanceManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; @@ -3380,7 +3378,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -4340,8 +4338,8 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe final int originalX = x; final int originalY = y; final int originalZ = z; - final int gtx = (originalX - World.MAP_MIN_X) >> 4; - final int gty = (originalY - World.MAP_MIN_Y) >> 4; + final int gtx = (originalX - World.WORLD_X_MIN) >> 4; + final int gty = (originalY - World.WORLD_Y_MIN) >> 4; if (isOnGeodataPath()) { try @@ -4364,7 +4362,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe && !(((curZ - z) > 300) && (distance < 300))) // Prohibit correcting destination if character wants to fall. { // location different if destination wasn't reached (or just z coord is different) - final Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(curX, curY, curZ, x, y, z, getInstanceId()); + final Location destiny = GeoEngine.getInstance().getValidLocation(curX, curY, curZ, x, y, z, getInstanceId()); x = destiny.getX(); y = destiny.getY(); z = destiny.getZ(); @@ -4378,7 +4376,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isAfraid() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId()); + m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceId()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { if (isPlayer() && !_isFlying && !isInWater) diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Summon.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Summon.java index d4542d0165..c50618d10e 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Summon.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/Summon.java @@ -29,7 +29,6 @@ import org.l2jmobius.gameserver.enums.Race; import org.l2jmobius.gameserver.enums.ShotType; import org.l2jmobius.gameserver.enums.Team; import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; import org.l2jmobius.gameserver.handler.IItemHandler; import org.l2jmobius.gameserver.handler.ItemHandler; import org.l2jmobius.gameserver.instancemanager.TerritoryWarManager; @@ -107,7 +106,7 @@ public abstract class Summon extends Playable final int x = owner.getX(); final int y = owner.getY(); final int z = owner.getZ(); - final Location location = GeoEngine.getInstance().canMoveToTargetLoc(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, owner.getInstanceId()); + final Location location = GeoEngine.getInstance().getValidLocation(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, owner.getInstanceId()); setXYZInvisible(location.getX(), location.getY(), location.getZ()); } @@ -646,7 +645,7 @@ public abstract class Summon extends Playable return false; } - if ((this != target) && skill.isPhysical() && Config.PATHFINDING && (GeoEnginePathfinding.getInstance().findPath(getX(), getY(), getZ(), target.getX(), target.getY(), target.getZ(), getInstanceId()) == null)) + if ((this != target) && skill.isPhysical() && Config.PATHFINDING && (GeoEngine.getInstance().findPath(getX(), getY(), getZ(), target.getX(), target.getY(), target.getZ(), getInstanceId()) == null)) { sendPacket(SystemMessageId.CANNOT_SEE_TARGET); return false; diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index f80c165743..d5ad05733f 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -8832,7 +8832,7 @@ public class PlayerInstance extends Playable { if (sklTargetType == TargetType.GROUND) { - if (!GeoEngine.getInstance().canSeeTarget(this, _currentSkillWorldPosition)) + if (!GeoEngine.getInstance().canSeeLocation(this, _currentSkillWorldPosition)) { sendPacket(SystemMessageId.CANNOT_SEE_TARGET); sendPacket(ActionFailed.STATIC_PACKET); diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java index 5504283200..6c4f8a0e68 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java @@ -1521,7 +1521,7 @@ public class ItemInstance extends WorldObject if (dropper != null) { - final Location dropDest = GeoEngine.getInstance().canMoveToTargetLoc(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, dropper.getInstanceId()); + final Location dropDest = GeoEngine.getInstance().getValidLocation(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, dropper.getInstanceId()); x = dropDest.getX(); y = dropDest.getY(); z = dropDest.getZ(); diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java index 3e9c72f851..709397b56d 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java @@ -114,7 +114,7 @@ public class ValidatePosition implements IClientIncomingPacket { if (player.isFalling(_z)) { - final int nearestZ = GeoEngine.getInstance().getHigherHeight(_x, _y, _z); + final int nearestZ = GeoEngine.getInstance().getHeight(_x, _y, _z); if (player.getZ() < nearestZ) { player.setXYZ(_x, _y, nearestZ); diff --git a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/util/GeoUtils.java index 4600a0fc53..19a979819e 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -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.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; @@ -30,21 +30,21 @@ public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getInstance().getWorldX(iter.x()); - final int wy = GeoEngine.getInstance().getWorldY(iter.y()); + final int wx = GeoEngine.getWorldX(iter.x()); + final int wy = GeoEngine.getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public final class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); - int wy = GeoEngine.getInstance().getWorldY(prevY); + int wx = GeoEngine.getWorldX(prevX); + int wy = GeoEngine.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.getInstance().getWorldX(curX); - wy = GeoEngine.getInstance().getWorldY(curY); + wx = GeoEngine.getWorldX(curX); + wy = GeoEngine.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().checkNearestNswe(x, y, z, nswe)) + if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0) { return Color.GREEN; } @@ -109,9 +109,8 @@ public final class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final GeoEngine ge = GeoEngine.getInstance(); - final int playerGx = ge.getGeoX(player.getX()); - final int playerGy = ge.getGeoY(player.getY()); + final int playerGx = GeoEngine.getGeoX(player.getX()); + final int playerGy = GeoEngine.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -135,32 +134,32 @@ public final class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = ge.getWorldX(gx); - final int y = ge.getWorldY(gy); - final int z = ge.getNearestZ(gx, gy, player.getZ()); + final int x = GeoEngine.getWorldX(gx); + final int y = GeoEngine.getWorldY(gy); + final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); + Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); 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, Cell.NSWE_EAST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); 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, Cell.NSWE_SOUTH); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); 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, Cell.NSWE_WEST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); 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); @@ -188,41 +187,41 @@ public final class GeoUtils { if (y > lastY) { - return Cell.NSWE_SOUTH_EAST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E; } else if (y < lastY) { - return Cell.NSWE_NORTH_EAST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E; } else { - return Cell.NSWE_EAST; + return GeoStructure.CELL_FLAG_E; } } else if (x < lastX) // west { if (y > lastY) { - return Cell.NSWE_SOUTH_WEST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W; } else if (y < lastY) { - return Cell.NSWE_NORTH_WEST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W; } else { - return Cell.NSWE_WEST; + return GeoStructure.CELL_FLAG_W; } } else // unchanged x { if (y > lastY) { - return Cell.NSWE_SOUTH; + return GeoStructure.CELL_FLAG_S; } else if (y < lastY) { - return Cell.NSWE_NORTH; + return GeoStructure.CELL_FLAG_N; } else { diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/config/GeoEngine.ini b/L2J_Mobius_Classic_2.0_Saviors/dist/game/config/GeoEngine.ini index 609e8a89a7..e740c678dd 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Classic_2.0_Saviors/dist/game/config/GeoEngine.ini @@ -6,33 +6,44 @@ # at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ 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 + # ================================================================= -# Path finding +# Pathfinding # ================================================================= # 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 +# an alternative path (e.g. walk around obstacle), default: True +PathFinding = True -# Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 -PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 +# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 -# Weight for nodes without obstacles far from walls -LowWeight = 0.5 +# Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 +MoveWeight = 10 +MoveWeightDiag = 14 -# Weight for nodes near walls -MediumWeight = 2 +# 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 -# Weight for nodes with obstacles -HighWeight = 3 +# 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. -# Default: LowWeight * sqrt(2) -DiagonalWeight = 0.707 +# Maximum number of generated nodes per one path-finding process, default 3500 +MaxIterations = 3500 # ================================================================= -# Other +# Line of Sight # ================================================================= -# Correct player Z after falling through the ground. -CorrectPlayerZ = False +# 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 diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/geodata/L2D_GeoEngine.patch b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/geodata/L2D_GeoEngine.patch deleted file mode 100644 index f8577e3a0e..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/geodata/L2D_GeoEngine.patch +++ /dev/null @@ -1,6035 +0,0 @@ -Index: dist/game/GeoDataConverter.bat -=================================================================== ---- dist/game/GeoDataConverter.bat (nonexistent) -+++ dist/game/GeoDataConverter.bat (working copy) -@@ -0,0 +1,6 @@ -+@echo off -+title L2D geodata converter -+ -+java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter -+ -+pause -Index: dist/game/GeoDataConverter.sh -=================================================================== ---- dist/game/GeoDataConverter.sh (nonexistent) -+++ dist/game/GeoDataConverter.sh (working copy) -@@ -0,0 +1,4 @@ -+#! /bin/sh -+ -+java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 -+ -Index: dist/game/config/GeoEngine.ini -=================================================================== ---- dist/game/config/GeoEngine.ini (revision 8409) -+++ dist/game/config/GeoEngine.ini (working copy) -@@ -1,6 +1,10 @@ - # ================================================================= - # Geodata - # ================================================================= -+# Because of real-time performance we are using geodata files only in -+# diagonal L2D format now (using filename e.g. 22_16.l2d). -+# L2D geodata can be obtained by conversion of existing L2J or L2OFF geodata. -+# Launch "GeoDataConverter.bat/sh" and follow instructions to start the conversion. - - # 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/ -@@ -13,6 +17,16 @@ - CoordSynchronize = 2 - - # ================================================================= -+# Path checking -+# ================================================================= -+ -+# 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 finding - # ================================================================= - -@@ -23,19 +37,23 @@ - # Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - --# Weight for nodes without obstacles far from walls --LowWeight = 0.5 -+# Base path weight, when moving from one node to another on axis direction, default: 10 -+BaseWeight = 10 - --# Weight for nodes near walls --MediumWeight = 2 -+# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -+DiagonalWeight = 14 - --# Weight for nodes with obstacles --HighWeight = 3 -+# When movement flags of target node is blocked to any direction, multiply movement weight by this multiplier. -+# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 10 -+ObstacleMultiplier = 10 - --# Weight for diagonal movement. --# Default: LowWeight * sqrt(2) --DiagonalWeight = 0.707 -+# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 20 -+# For proper function must be higher than BaseWeight and/or DiagonalWeight. -+HeuristicWeight = 20 - -+# Maximum number of generated nodes per one path-finding process, default 3500 -+MaxIterations = 3500 -+ - # ================================================================= - # Other - # ================================================================= -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (working copy) -@@ -55,9 +55,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -@@ -73,9 +72,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (working copy) -@@ -18,11 +18,11 @@ - - import java.util.List; - --import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -+import org.l2jmobius.gameserver.geoengine.GeoEngine; - import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -+import org.l2jmobius.gameserver.network.SystemMessageId; - import org.l2jmobius.gameserver.util.BuilderUtil; - - public class AdminPathNode implements IAdminCommandHandler -@@ -37,29 +37,30 @@ - { - if (command.equals("admin_path_find")) - { -- if (!Config.PATHFINDING) -- { -- BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); -- return true; -- } - if (activeChar.getTarget() != null) - { -- final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); -+ final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); - if (path == null) - { -- BuilderUtil.sendSysMessage(activeChar, "No Route!"); -- return true; -+ BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); - } -- for (AbstractNodeLoc a : path) -+ else - { -- BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); -+ for (Location point : path) -+ { -+ BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); -+ } - } - } - else - { -- BuilderUtil.sendSysMessage(activeChar, "No Target!"); -+ activeChar.sendPacket(SystemMessageId.INVALID_TARGET); - } - } -+ else -+ { -+ return false; -+ } - return true; - } - -Index: java/org/l2jmobius/Config.java -=================================================================== ---- java/org/l2jmobius/Config.java (revision 8409) -+++ java/org/l2jmobius/Config.java (working copy) -@@ -32,7 +32,6 @@ - import java.net.UnknownHostException; - import java.nio.charset.StandardCharsets; - import java.nio.file.Files; --import java.nio.file.Path; - import java.nio.file.Paths; - import java.time.Duration; - import java.util.ArrayList; -@@ -966,14 +965,17 @@ - // -------------------------------------------------- - // GeoEngine - // -------------------------------------------------- -- public static Path GEODATA_PATH; -+ public static String GEODATA_PATH; -+ public static int COORD_SYNCHRONIZE; -+ public static int PART_OF_CHARACTER_HEIGHT; -+ public static int MAX_OBSTACLE_HEIGHT; - public static boolean PATHFINDING; - public static String PATHFIND_BUFFERS; -- public static float LOW_WEIGHT; -- public static float MEDIUM_WEIGHT; -- public static float HIGH_WEIGHT; -- public static float DIAGONAL_WEIGHT; -- public static int COORD_SYNCHRONIZE; -+ public static int BASE_WEIGHT; -+ public static int DIAGONAL_WEIGHT; -+ public static int HEURISTIC_WEIGHT; -+ public static int OBSTACLE_MULTIPLIER; -+ public static int MAX_ITERATIONS; - public static boolean CORRECT_PLAYER_Z; - - /** Attribute System */ -@@ -2568,14 +2570,17 @@ - - // Load GeoEngine config file (if exists) - final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); -- GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); -+ GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); -+ COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); -+ MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); - PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -- LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); -- MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); -- HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); -- DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); -- COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); -+ DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); -+ OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); -+ HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); -+ MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); - CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); - - // Load AllowedPlayerRaces config file (if exists) -Index: java/org/l2jmobius/gameserver/geoengine/GeoEngine.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (revision 8435) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (working copy) -@@ -16,100 +16,90 @@ - */ - package org.l2jmobius.gameserver.geoengine; - --import java.io.IOException; -+import java.io.File; - import java.io.RandomAccessFile; - import java.nio.ByteOrder; --import java.nio.channels.FileChannel.MapMode; --import java.nio.file.Files; --import java.nio.file.Path; --import java.util.concurrent.atomic.AtomicReferenceArray; --import java.util.logging.Level; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.List; - import java.util.logging.Logger; - - import org.l2jmobius.Config; - import org.l2jmobius.gameserver.data.xml.DoorData; - import org.l2jmobius.gameserver.data.xml.FenceData; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; --import org.l2jmobius.gameserver.geoengine.geodata.IRegion; --import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; --import org.l2jmobius.gameserver.geoengine.geodata.Region; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.World; - import org.l2jmobius.gameserver.model.WorldObject; -+import org.l2jmobius.gameserver.model.actor.Creature; - import org.l2jmobius.gameserver.model.instancezone.Instance; --import org.l2jmobius.gameserver.model.interfaces.ILocational; --import org.l2jmobius.gameserver.util.GeoUtils; --import org.l2jmobius.gameserver.util.LinePointIterator; --import org.l2jmobius.gameserver.util.LinePointIterator3D; -+import org.l2jmobius.gameserver.util.MathUtil; - - /** -- * @author -Nemesiss-, HorridoJoho -+ * @author Hasha - */ - public class GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); -+ protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - -- private static final int WORLD_MIN_X = -655360; -- private static final int WORLD_MIN_Y = -589824; -- private static final int WORLD_MIN_Z = -16384; -+ private final ABlock[][] _blocks; -+ private final BlockNull _nullBlock; - -- /** 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; -- -- /** The regions array */ -- private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); -- -- private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; -- private static final int ELEVATED_SEE_OVER_DISTANCE = 2; -- private static final int MAX_SEE_OVER_HEIGHT = 48; -- -- protected GeoEngine() -+ /** -+ * GeoEngine constructor. Loads all geodata files of chosen geodata format. -+ */ -+ public GeoEngine() - { - LOGGER.info("GeoEngine: Initializing..."); -- for (int i = 0; i < _regions.length(); i++) -- { -- _regions.set(i, NullRegion.INSTANCE); -- } - -+ // initialize block container -+ _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; -+ -+ // load null block -+ _nullBlock = new BlockNull(); -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files according to geoengine config setup - int loaded = 0; -- try -+ long fileSize = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { -- for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { -- for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) -+ final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); -+ if (f.exists() && !f.isDirectory()) - { -- final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); -- if (Files.exists(geoFilePath)) -+ // region file is load-able, try to load it -+ if (loadGeoBlocks(rx, ry)) - { -- try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) -- { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -- loaded++; -- } -+ loaded++; -+ fileSize += f.length(); - } - } -+ else -+ { -+ // region file is not load-able, load null blocks -+ loadNullBlocks(rx, ry); -+ } - } - } -- catch (Exception e) -+ -+ LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -+ if (loaded > 0) - { -- LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); -- System.exit(1); -+ LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); - } - -- LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -- -- // Avoid wrong configs when no files are loaded. -+ // avoid wrong configs when no files are loaded - if (loaded == 0) - { - if (Config.PATHFINDING) -@@ -123,619 +113,820 @@ - LOGGER.info("GeoEngine: Forcing CoordSynchronize setting to -1."); - } - } -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); - } - - /** -- * @param geoX -- * @param geoY -- * @return the region -+ * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. -+ * @return boolean : True, when geodata file was loaded without problem. - */ -- private IRegion getRegion(int geoX, int geoY) -+ private final boolean loadGeoBlocks(int regionX, int regionY) - { -- final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); -- if ((region < 0) || (region >= _regions.length())) -+ final String filename = String.format(GeoFormat.L2D.getFilename(), regionX, regionY); -+ -+ // standard load -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) - { -- return null; -+ // initialize file buffer -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, GeoFormat.L2D); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ } -+ -+ // check data consistency -+ if (buffer.remaining() > 0) -+ { -+ LOGGER.warning("GeoEngine: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ } -+ -+ // loading was successful -+ return true; - } -- return _regions.get(region); -- } -- -- /** -- * @param filePath -- * @param regionX -- * @param regionY -- * @throws IOException -- */ -- 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")) -+ catch (Exception e) - { -- _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -+ // an error occured while loading, load null blocks -+ LOGGER.warning("GeoEngine: Error while loading " + filename + " region file."); -+ LOGGER.warning(e.getMessage()); -+ -+ // replace whole region file with null blocks -+ loadNullBlocks(regionX, regionY); -+ -+ // loading was not successful -+ return false; - } - } - - /** -- * @param regionX -- * @param regionY -+ * Loads null blocks. Used when no region file is detected or an error occurs during loading. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. - */ -- public void unloadRegion(int regionX, int regionY) -+ private final void loadNullBlocks(int regionX, int regionY) - { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); -- } -- -- /** -- * @param geoX -- * @param geoY -- * @return if geodata exist -- */ -- public boolean hasGeoPos(int geoX, int geoY) -- { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // load all null blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { -- return false; -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[blockX + ix][blockY + iy] = _nullBlock; -+ } - } -- return region.hasGeo(); - } - -+ // GEODATA - GENERAL -+ - /** -- * Checks the specified position for available geodata. -- * @param x the world x -- * @param y the world y -- * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise -+ * Converts world X to geodata X. -+ * @param worldX -+ * @return int : Geo X - */ -- public boolean hasGeo(int x, int y) -+ public static int getGeoX(int worldX) - { -- return hasGeoPos(getGeoX(x), getGeoY(y)); -+ return (MathUtil.limit(worldX, World.MAP_MIN_X, World.MAP_MAX_X) - World.MAP_MIN_X) >> 4; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe check -+ * Converts world Y to geodata Y. -+ * @param worldY -+ * @return int : Geo Y - */ -- public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) -+ public static int getGeoY(int worldY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return true; -- } -- return region.checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(worldY, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.MAP_MIN_Y) >> 4; - } - - /** -+ * Converts geodata X to world X. - * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe anti-corner cut -+ * @return int : World X - */ -- public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) -+ public static int getWorldX(int geoX) - { -- boolean can = true; -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- return can && checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(geoX, 0, GeoStructure.GEO_CELLS_X) << 4) + World.MAP_MIN_X + 8; - } - - /** -- * @param geoX -+ * Converts geodata Y to world Y. - * @param geoY -- * @param worldZ -- * @return the nearest Z value -+ * @return int : World Y - */ -- public int getNearestZ(int geoX, int geoY, int worldZ) -+ public static int getWorldY(int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return worldZ; -- } -- return region.getNearestZ(geoX, geoY, worldZ); -+ return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next lower Z value -+ * Returns block of geodata on given coordinates. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return {@link ABlock} : Block of geodata. - */ -- public int getNextLowerZ(int geoX, int geoY, int worldZ) -+ private final ABlock getBlock(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final int x = geoX / GeoStructure.BLOCK_CELLS_X; -+ final int y = geoY / GeoStructure.BLOCK_CELLS_Y; -+ -+ // if x or y is out of array return null -+ if ((x > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) - { -- return worldZ; -+ return _blocks[x][y]; - } -- return region.getNextLowerZ(geoX, geoY, worldZ); -+ return null; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next higher Z value -+ * Check if geo coordinates has geo. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return boolean : True, if given geo coordinates have geodata - */ -- public int getNextHigherZ(int geoX, int geoY, int worldZ) -+ public boolean hasGeoPos(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final ABlock block = getBlock(geoX, geoY); -+ if (block == null) // null block check - { -- return worldZ; -+ // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) -+ // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); -+ return false; - } -- return region.getNextHigherZ(geoX, geoY, worldZ); -+ return block.hasGeoPos(); - } - - /** -- * Gets the Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the height of cell, which is closest to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, closest to given coordinates. - */ -- public int getHeight(int x, int y, int z) -+ public short getHeightNearest(int geoX, int geoY, int worldZ) - { -- return getNearestZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getHeightNearest(geoX, geoY, worldZ) : (short) worldZ; - } - - /** -- * Gets the next lower Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the NSWE flag byte of cell, which is closes to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. - */ -- public int getLowerHeight(int x, int y, int z) -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) - { -- return getNextLowerZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getNsweNearest(geoX, geoY, worldZ) : (byte) 0xFF; - } - - /** -- * Gets the next higher Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Check if world coordinates has geo. -+ * @param worldX : World X -+ * @param worldY : World Y -+ * @return boolean : True, if given world coordinates have geodata - */ -- public int getHigherHeight(int x, int y, int z) -+ public boolean hasGeo(int worldX, int worldY) - { -- return getNextHigherZ(getGeoX(x), getGeoY(y), z); -+ return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); - } - - /** -- * @param worldX -- * @return the geo X -+ * Returns closest Z coordinate according to geodata. -+ * @param worldX : world x -+ * @param worldY : world y -+ * @param worldZ : world z -+ * @return short : nearest Z coordinates according to geodata - */ -- public int getGeoX(int worldX) -+ public short getHeight(int worldX, int worldY, int worldZ) - { -- return (worldX - WORLD_MIN_X) / 16; -+ return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - -- /** -- * @param worldY -- * @return the geo Y -- */ -- public int getGeoY(int worldY) -- { -- return (worldY - WORLD_MIN_Y) / 16; -- } -+ // PATHFINDING - - /** -- * @param worldZ -- * @return the geo Z -+ * Check line of sight from {@link WorldObject} to {@link WorldObject}. -+ * @param origin : The origin object. -+ * @param target : The target object. -+ * @return {@code boolean} : True if origin can see target - */ -- public int getGeoZ(int worldZ) -+ public boolean canSeeTarget(WorldObject origin, WorldObject target) - { -- return (worldZ - WORLD_MIN_Z) / 16; -- } -- -- /** -- * @param geoX -- * @return the world X -- */ -- public int getWorldX(int geoX) -- { -- return (geoX * 16) + WORLD_MIN_X + 8; -- } -- -- /** -- * @param geoY -- * @return the world Y -- */ -- public int getWorldY(int geoY) -- { -- return (geoY * 16) + WORLD_MIN_Y + 8; -- } -- -- /** -- * @param geoZ -- * @return the world Z -- */ -- public int getWorldZ(int geoZ) -- { -- return (geoZ * 16) + WORLD_MIN_Z + 8; -- } -- -- /** -- * Can see target. Doors as target always return true. Checks doors between. -- * @param cha the character -- * @param target the target -- * @return {@code true} if the character can see the target (LOS), {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, WorldObject target) -- { -- if (target.isDoor()) -+ if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) - { -- // Can always see doors. - return true; - } -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param cha the character -- * @param worldPosition the world position -- * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) -- { -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param world -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @param tworld the target's instanceId -- * @return -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) -- { -- return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instance -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) -- { -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) -+ -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = target.getX(); -+ final int ty = target.getY(); -+ final int tz = target.getZ(); -+ -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } -- return canSeeTarget(x, y, z, tx, ty, tz); -- } -- -- /** -- * @param prevX -- * @param prevY -- * @param prevGeoZ -- * @param curX -- * @param curY -- * @param nswe -- * @return the LOS Z value -- */ -- private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) -- { -- if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { -- throw new RuntimeException("Multiple directions!"); -+ return false; - } - -- if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- return getNearestZ(curX, curY, prevGeoZ); -+ return true; - } - -- return getNextHigherZ(curX, curY, prevGeoZ); -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) -+ { -+ return true; -+ } -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) -+ { -+ return goz == gtz; -+ } -+ -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) -+ { -+ oheight = ((Creature) origin).getCollisionHeight() * 2; -+ } -+ -+ double theight = 0; -+ if (target.isCreature()) -+ { -+ theight = ((Creature) target).getCollisionHeight() * 2; -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); - } - - /** -- * Can see target. Does not check doors between. -- * @param xValue the x coordinate -- * @param yValue the y coordinate -- * @param zValue the z coordinate -- * @param txValue the target's x coordinate -- * @param tyValue the target's y coordinate -- * @param tzValue the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -+ * Check line of sight from {@link WorldObject} to {@link Location}. -+ * @param origin : The origin object. -+ * @param position : The target position. -+ * @return {@code boolean} : True if object can see position - */ -- public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) -+ public boolean canSeeTarget(WorldObject origin, Location position) - { -- int x = xValue; -- int y = yValue; -- int tx = txValue; -- int ty = tyValue; -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = position.getX(); -+ final int ty = position.getY(); -+ final int tz = position.getZ(); - -- int geoX = getGeoX(x); -- int geoY = getGeoY(y); -- int tGeoX = getGeoX(tx); -- int tGeoY = getGeoY(ty); -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) -+ { -+ return false; -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) -+ { -+ return false; -+ } - -- int z = getNearestZ(geoX, geoY, zValue); -- int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- // Fastpath. -- if ((geoX == tGeoX) && (geoY == tGeoY)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- if (hasGeoPos(tGeoX, tGeoY)) -- { -- return z == tz; -- } - return true; - } - -- if (tz > z) -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) - { -- int tmp = tx; -- tx = x; -- x = tmp; -- -- tmp = ty; -- ty = y; -- y = tmp; -- -- tmp = tz; -- tz = z; -- z = tmp; -- -- tmp = tGeoX; -- tGeoX = geoX; -- geoX = tmp; -- -- tmp = tGeoY; -- tGeoY = geoY; -- geoY = tmp; -+ return goz == gtz; - } - -- final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); -- // First point is guaranteed to be available, skip it, we can always see our own position. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- final int prevZ = pointIter.z(); -- int prevGeoZ = prevZ; -- int ptIndex = 0; -- while (pointIter.next()) -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight(); -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); -+ } -+ -+ /** -+ * Simple check for origin to target visibility. -+ * @param goxValue : origin X geodata coordinate -+ * @param goyValue : origin Y geodata coordinate -+ * @param gozValue : origin Z geodata coordinate -+ * @param oheight : origin height (if instance of {@link Character}) -+ * @param gtxValue : target X geodata coordinate -+ * @param gtyValue : target Y geodata coordinate -+ * @param gtzValue : target Z geodata coordinate -+ * @param theight : target height (if instance of {@link Character}) -+ * @param instance -+ * @return {@code boolean} : True, when target can be seen. -+ */ -+ private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) -+ { -+ int goz = gozValue; -+ int gtz = gtzValue; -+ int gox = goxValue; -+ int goy = goyValue; -+ int gtx = gtxValue; -+ int gty = gtyValue; -+ -+ // get line of sight Z coordinates -+ double losoz = goz + ((oheight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ double lostz = gtz + ((theight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ -+ // get X delta and signum -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirox = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; -+ final byte dirtx = sx > 0 ? GeoStructure.CELL_FLAG_W : GeoStructure.CELL_FLAG_E; -+ -+ // get Y delta and signum -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte diroy = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ final byte dirty = sy > 0 ? GeoStructure.CELL_FLAG_N : GeoStructure.CELL_FLAG_S; -+ -+ // get Z delta -+ final int dm = Math.max(dx, dy); -+ final double dz = (lostz - losoz) / dm; -+ -+ // get direction flag for diagonal movement -+ final byte diroxy = getDirXY(dirox, diroy); -+ final byte dirtxy = getDirXY(dirtx, dirty); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte diro; -+ byte dirt; -+ -+ // initialize node values -+ int nox = gox; -+ int noy = goy; -+ int ntx = gtx; -+ int nty = gty; -+ byte nsweo = getNsweNearest(gox, goy, goz); -+ byte nswet = getNsweNearest(gtx, gty, gtz); -+ -+ // loop -+ ABlock block; -+ int index; -+ for (int i = 0; i < ((dm + 1) / 2); i++) -+ { -+ // reset direction flag -+ diro = 0; -+ dirt = 0; - -- if ((curX == prevX) && (curY == prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- continue; -+ // calculate next point XY coordinates -+ d -= dy; -+ d += dx; -+ nox += sx; -+ ntx -= sx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroxy; -+ dirt |= dirtxy; - } -+ else if (e2 > -dy) -+ { -+ // calculate next point X coordinate -+ d -= dy; -+ nox += sx; -+ ntx -= sx; -+ diro |= dirox; -+ dirt |= dirtx; -+ } -+ else if (e2 < dx) -+ { -+ // calculate next point Y coordinate -+ d += dx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroy; -+ dirt |= dirty; -+ } - -- final int beeCurZ = pointIter.z(); -- int curGeoZ = prevGeoZ; -- -- // Check if the position has geodata. -- if (hasGeoPos(curX, curY)) - { -- final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); -- curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); -- int maxHeight; -- if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) -+ // get block of the next cell -+ block = getBlock(nox, noy); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nsweo & diro) == 0) - { -- maxHeight = z + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); - } - else - { -- maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); - } - -- boolean canSeeThrough = false; -- if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) -+ // layer does not exist, return -+ if (index == -1) - { -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else -- { -- canSeeThrough = true; -- } -+ return false; - } - -- if (!canSeeThrough) -+ // get layer and next line of sight Z coordinate -+ goz = block.getHeight(index); -+ losoz += dz; -+ -+ // perform line of sight check, return when fails -+ if ((goz - losoz) > Config.MAX_OBSTACLE_HEIGHT) - { - return false; - } -+ -+ // get layer nswe -+ nsweo = block.getNswe(index); - } -+ { -+ // get block of the next cell -+ block = getBlock(ntx, nty); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nswet & dirt) == 0) -+ { -+ index = block.getIndexAbove(ntx, nty, gtz - GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ else -+ { -+ index = block.getIndexBelow(ntx, nty, gtz + GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ -+ // layer does not exist, return -+ if (index == -1) -+ { -+ return false; -+ } -+ -+ // get layer and next line of sight Z coordinate -+ gtz = block.getHeight(index); -+ lostz -= dz; -+ -+ // perform line of sight check, return when fails -+ if ((gtz - lostz) > Config.MAX_OBSTACLE_HEIGHT) -+ { -+ return false; -+ } -+ -+ // get layer nswe -+ nswet = block.getNswe(index); -+ } - -- prevX = curX; -- prevY = curY; -- prevGeoZ = curGeoZ; -- ++ptIndex; -+ // update coords -+ gox = nox; -+ goy = noy; -+ gtx = ntx; -+ gty = nty; - } - -- return true; -+ // when iteration is completed, compare final Z coordinates -+ return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); - } - - /** -- * Move check. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param zValue the z coordinate -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tzValue the target's z coordinate -- * @param instance the instance -- * @return the last Location (x,y,z) where player can walk - just before wall -+ * Check movement from coordinates to coordinates. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {code boolean} : True if target coordinates are reachable from origin coordinates - */ -- public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) -+ public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- final int geoX = getGeoX(x); -- final int geoY = getGeoY(y); -- final int z = getNearestZ(geoX, geoY, zValue); -- final int tGeoX = getGeoX(tx); -- final int tGeoY = getGeoY(ty); -- final int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } -- if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = z; -+ // perform geodata check -+ final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); -+ return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); -+ } -+ -+ /** -+ * Check movement from origin to target. Returns last available point in the checked path. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {@link Location} : Last point where object can walk (just before wall) -+ */ -+ public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) -+ { -+ return new Location(ox, oy, oz); -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) -+ { -+ return new Location(ox, oy, oz); -+ } - -- while (pointIter.next()) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -- -- if (hasGeoPos(prevX, prevY)) -- { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -- { -- // Can't move, return previous location. -- return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); -- } -- } -- -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return new Location(tx, ty, tz); - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != tz)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- // Different floors, return start location. -- return new Location(x, y, z); -+ return new Location(tx, ty, tz); - } - -- return new Location(tx, ty, tz); -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) -+ { -+ return new Location(tx, ty, tz); -+ } -+ -+ // perform geodata check -+ return checkMove(gox, goy, goz, gtx, gty, gtz, instance); - } - - /** -- * Checks if its possible to move from one location to another. -- * @param fromX the X coordinate to start checking from -- * @param fromY the Y coordinate to start checking from -- * @param fromZvalue the Z coordinate to start checking from -- * @param toX the X coordinate to end checking at -- * @param toY the Y coordinate to end checking at -- * @param toZvalue the Z coordinate to end checking at -- * @param instance the instance -- * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise -+ * With this method you can check if a position is visible or can be reached by beeline movement.
-+ * Target X and Y reachable and Z is on same floor: -+ *
    -+ *
  • Location of the target with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y reachable but Z is on another floor: -+ *
    -+ *
  • Location of the origin with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y not reachable: -+ *
    -+ *
  • Last accessible location in destination to target.
  • -+ *
-+ * @param gox : origin X geodata coordinate -+ * @param goy : origin Y geodata coordinate -+ * @param goz : origin Z geodata coordinate -+ * @param gtx : target X geodata coordinate -+ * @param gty : target Y geodata coordinate -+ * @param gtz : target Z geodata coordinate -+ * @param instance -+ * @return {@link GeoLocation} : The last allowed point of movement. - */ -- public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) -+ protected final GeoLocation checkMove(int gox, int goy, int goz, int gtx, int gty, int gtz, Instance instance) - { -- final int geoX = getGeoX(fromX); -- final int geoY = getGeoY(fromY); -- final int fromZ = getNearestZ(geoX, geoY, fromZvalue); -- final int tGeoX = getGeoX(toX); -- final int tGeoY = getGeoY(toY); -- final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); -- -- if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) -+ if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } -- if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) -+ if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = fromZ; -+ // get X delta, signum and direction flag -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirX = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; - -- while (pointIter.next()) -+ // get Y delta, signum and direction flag -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte dirY = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ -+ // get direction flag for diagonal movement -+ final byte dirXY = getDirXY(dirX, dirY); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte direction; -+ -+ // load pointer coordinates -+ int gpx = gox; -+ int gpy = goy; -+ int gpz = goz; -+ -+ // load next pointer -+ int nx = gpx; -+ int ny = gpy; -+ -+ // loop -+ int count = 0; -+ while (count++ < Config.MAX_ITERATIONS) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -+ direction = 0; - -- if (hasGeoPos(prevX, prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -+ d -= dy; -+ d += dx; -+ nx += sx; -+ ny += sy; -+ direction |= dirXY; -+ } -+ else if (e2 > -dy) -+ { -+ d -= dy; -+ nx += sx; -+ direction |= dirX; -+ } -+ else if (e2 < dx) -+ { -+ d += dx; -+ ny += sy; -+ direction |= dirY; -+ } -+ -+ // obstacle found, return -+ if ((getNsweNearest(gpx, gpy, gpz) & direction) == 0) -+ { -+ return new GeoLocation(gpx, gpy, gpz); -+ } -+ -+ // update pointer coordinates -+ gpx = nx; -+ gpy = ny; -+ gpz = getHeightNearest(nx, ny, gpz); -+ -+ // target coordinates reached -+ if ((gpx == gtx) && (gpy == gty)) -+ { -+ if (gpz == gtz) - { -- return false; -+ // path found, Z coordinates are okay, return target point -+ return new GeoLocation(gtx, gty, gtz); - } -+ -+ // path found, Z coordinates are not okay, return last good point -+ return new GeoLocation(gpx, gpy, gpz); - } -+ } -+ -+ return new GeoLocation(gox, goy, goz); -+ } -+ -+ /** -+ * Returns diagonal NSWE flag format of combined two NSWE flags. -+ * @param dirX : X direction NSWE flag -+ * @param dirY : Y direction NSWE flag -+ * @return byte : NSWE flag of combined direction -+ */ -+ private static byte getDirXY(byte dirX, byte dirY) -+ { -+ // check axis directions -+ if (dirY == GeoStructure.CELL_FLAG_N) -+ { -+ if (dirX == GeoStructure.CELL_FLAG_W) -+ { -+ return GeoStructure.CELL_FLAG_NW; -+ } - -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return GeoStructure.CELL_FLAG_NE; - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) -+ if (dirX == GeoStructure.CELL_FLAG_W) - { -- // Different floors. -- return false; -+ return GeoStructure.CELL_FLAG_SW; - } - -- return true; -+ return GeoStructure.CELL_FLAG_SE; - } - -+ /** -+ * Returns the list of location objects as a result of complete path calculation. -+ * @param ox : origin x -+ * @param oy : origin y -+ * @param oz : origin z -+ * @param tx : target x -+ * @param ty : target y -+ * @param tz : target z -+ * @param instance -+ * @return {@code List} : complete path from nodes -+ */ -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ return null; -+ } -+ -+ /** -+ * Returns the instance of the {@link GeoEngine}. -+ * @return {@link GeoEngine} : The instance. -+ */ - public static GeoEngine getInstance() - { - return SingletonHolder.INSTANCE; -@@ -743,6 +934,6 @@ - - private static class SingletonHolder - { -- protected static final GeoEngine INSTANCE = new GeoEngine(); -+ protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); - } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (working copy) -@@ -20,88 +20,84 @@ - import java.util.LinkedList; - import java.util.List; - import java.util.ListIterator; --import java.util.logging.Level; --import java.util.logging.Logger; - - import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; --import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; --import org.l2jmobius.gameserver.model.World; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -+import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.instancezone.Instance; - - /** -- * @author -Nemesiss- -+ * @author Hasha - */ --public class GeoEnginePathfinding -+final class GeoEnginePathfinding extends GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); -+ // pre-allocated buffers -+ private final BufferHolder[] _buffers; - -- private BufferInfo[] _buffers; -- - protected GeoEnginePathfinding() - { -- try -+ super(); -+ -+ final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ _buffers = new BufferHolder[array.length]; -+ int count = 0; -+ for (int i = 0; i < array.length; i++) - { -- final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ final String buf = array[i]; -+ final String[] args = buf.split("x"); - -- _buffers = new BufferInfo[array.length]; -- -- String buf; -- String[] args; -- for (int i = 0; i < array.length; i++) -+ try - { -- buf = array[i]; -- args = buf.split("x"); -- if (args.length != 2) -- { -- throw new Exception("Invalid buffer definition: " + buf); -- } -- -- _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); -+ final int size = Integer.parseInt(args[1]); -+ count += size; -+ _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); - } -+ catch (Exception e) -+ { -+ LOGGER.warning("GeoEnginePathfinding: Can not load buffer setting: " + buf); -+ } - } -- catch (Exception e) -- { -- LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); -- throw new Error("CellPathFinding: load aborted"); -- } -+ -+ LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); - } - -- public boolean pathNodesExist(short regionoffset) -+ @Override -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- return false; -- } -- -- public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) -- { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -- if (!GeoEngine.getInstance().hasGeo(x, y)) -+ // get origin and check existing geo coords -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { - 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)) -+ -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coords -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { - 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)))); -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // Prepare buffer for pathfinding calculations -+ final NodeBuffer buffer = getBuffer(64 + (2 * Math.max(Math.abs(gox - gtx), Math.abs(goy - gty)))); - if (buffer == null) - { - return null; - } - -- List path = null; -+ // find path -+ List path = null; - try - { -- final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); -- -+ final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); - if (result == null) - { - return null; -@@ -125,33 +121,45 @@ - return path; - } - -- int currentX, currentY, currentZ; -- ListIterator middlePoint; -+ // get path list iterator -+ final ListIterator point = path.listIterator(); - -- middlePoint = path.listIterator(); -- currentX = x; -- currentY = y; -- currentZ = z; -+ // get node A (origin) -+ int nodeAx = gox; -+ int nodeAy = goy; -+ short nodeAz = goz; - -- while (middlePoint.hasNext()) -+ // get node B -+ GeoLocation nodeB = (GeoLocation) point.next(); -+ -+ // iterate thought the path to optimize it -+ int count = 0; -+ while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) - { -- final AbstractNodeLoc locMiddle = middlePoint.next(); -- if (!middlePoint.hasNext()) -- { -- break; -- } -+ // get node C -+ final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - -- final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); -- if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) -+ // check movement from node A to node C -+ final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); -+ if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) - { -- middlePoint.remove(); -+ // can move from node A to node C -+ -+ // remove node B -+ point.remove(); - } - else - { -- currentX = locMiddle.getX(); -- currentY = locMiddle.getY(); -- currentZ = locMiddle.getZ(); -+ // can not move from node A to node C -+ -+ // set node A (node B is part of path, update A coordinates) -+ nodeAx = nodeB.getGeoX(); -+ nodeAy = nodeB.getGeoY(); -+ nodeAz = (short) nodeB.getZ(); - } -+ -+ // set node B -+ nodeB = (GeoLocation) point.next(); - } - - return path; -@@ -158,144 +166,102 @@ - } - - /** -- * Convert geodata position to pathnode position -- * @param geo_pos -- * @return pathnode position -+ * Create list of node locations as result of calculated buffer node tree. -+ * @param node : the entry point -+ * @return List : list of node location - */ -- public short getNodePos(int geo_pos) -+ private static List constructPath(Node node) - { -- return (short) (geo_pos >> 3); // OK? -- } -- -- /** -- * Convert node position to pathnode block position -- * @param node_pos -- * @return pathnode block position (0...255) -- */ -- public short getNodeBlock(int node_pos) -- { -- return (short) (node_pos % 256); -- } -- -- public byte getRegionX(int node_pos) -- { -- return (byte) ((node_pos >> 8) + World.TILE_X_MIN); -- } -- -- public byte getRegionY(int node_pos) -- { -- return (byte) ((node_pos >> 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 node_x rx -- * @return -- */ -- public int calculateWorldX(short node_x) -- { -- return World.MAP_MIN_X + (node_x * 128) + 48; -- } -- -- /** -- * Convert pathnode y to World y position -- * @param node_y -- * @return -- */ -- public int calculateWorldY(short node_y) -- { -- return World.MAP_MIN_Y + (node_y * 128) + 48; -- } -- -- private List constructPath(AbstractNode nodeValue) -- { -- final LinkedList path = new LinkedList<>(); -- int previousDirectionX = Integer.MIN_VALUE; -- int previousDirectionY = Integer.MIN_VALUE; -- int directionX, directionY; -+ // create empty list -+ final LinkedList list = new LinkedList<>(); - -- AbstractNode node = nodeValue; -- while (node.getParent() != null) -+ // set direction X/Y -+ int dx = 0; -+ int dy = 0; -+ -+ // get target parent -+ Node target = node; -+ Node parent = target.getParent(); -+ -+ // while parent exists -+ int count = 0; -+ while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { -- directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); -- directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); -+ // get parent <> target direction X/Y -+ final int nx = parent.getLoc().getGeoX() - target.getLoc().getGeoX(); -+ final int ny = parent.getLoc().getGeoY() - target.getLoc().getGeoY(); - -- // only add a new route point if moving direction changes -- if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) -+ // direction has changed? -+ if ((dx != nx) || (dy != ny)) - { -- previousDirectionX = directionX; -- previousDirectionY = directionY; -+ // add node to the beginning of the list -+ list.addFirst(target.getLoc()); - -- path.addFirst(node.getLoc()); -- node.setLoc(null); -+ // update direction X/Y -+ dx = nx; -+ dy = ny; - } - -- node = node.getParent(); -+ // move to next node, set target and get its parent -+ target = parent; -+ parent = target.getParent(); - } - -- return path; -+ // return list -+ return list; - } - -- private CellNodeBuffer alloc(int size) -+ /** -+ * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer. -+ * @param size : pre-calculated minimal required size -+ * @return NodeBuffer : buffer -+ */ -+ private final NodeBuffer getBuffer(int size) - { -- CellNodeBuffer current = null; -- for (BufferInfo i : _buffers) -+ NodeBuffer current = null; -+ for (BufferHolder holder : _buffers) - { -- if (i.mapSize >= size) -+ // Find proper size of buffer -+ if (holder._size < size) - { -- for (CellNodeBuffer buf : i.buffer) -+ continue; -+ } -+ -+ // Find unlocked NodeBuffer -+ for (NodeBuffer buffer : holder._buffer) -+ { -+ if (!buffer.isLocked()) - { -- if (buf.lock()) -- { -- current = buf; -- break; -- } -+ continue; - } -- if (current != null) -- { -- break; -- } - -- // not found, allocate temporary buffer -- current = new CellNodeBuffer(i.mapSize); -- current.lock(); -- if (i.buffer.size() < i.count) -- { -- i.buffer.add(current); -- break; -- } -+ return buffer; - } -+ -+ // NodeBuffer not found, allocate temporary buffer -+ current = new NodeBuffer(holder._size); -+ current.isLocked(); - } - - return current; - } - -- private static final class BufferInfo -+ /** -+ * NodeBuffer container with specified size and count of separate buffers. -+ */ -+ private static final class BufferHolder - { -- final int mapSize; -- final int count; -- ArrayList buffer; -+ final int _size; -+ List _buffer; - -- public BufferInfo(int size, int cnt) -+ public BufferHolder(int size, int count) - { -- mapSize = size; -- count = cnt; -- buffer = new ArrayList<>(count); -+ _size = size; -+ _buffer = new ArrayList<>(count); -+ for (int i = 0; i < count; i++) -+ { -+ _buffer.add(new NodeBuffer(size)); -+ } - } - } -- -- public static GeoEnginePathfinding getInstance() -- { -- return SingletonHolder.INSTANCE; -- } -- -- private static class SingletonHolder -- { -- protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); -- } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (working copy) -@@ -0,0 +1,197 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @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 height of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getHeightNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first above given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, above given coordinates. -+ */ -+ public abstract short getHeightAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first below given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is closest to given coordinates. -+ * @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 the NSWE flag byte of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getNsweNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first above given coordinates. -+ * @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 getNsweAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first below given coordinates. -+ * @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 getNsweBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is closes layer to given coordinates. -+ * @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. -+ * @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 above given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is first layer below given coordinates. -+ * @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 index to data of the cell, which is first layer below given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexBelowOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeight(int index); -+ -+ /** -+ * Returns the height of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightOriginal(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNswe(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNsweOriginal(int index); -+ -+ /** -+ * Sets the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @param nswe : New NSWE flag byte. -+ */ -+ public abstract void setNswe(int index, byte nswe); -+ -+ /** -+ * Saves the block in L2D format to {@link BufferedOutputStream}. Used only for L2D geodata conversion. -+ * @param stream : The stream. -+ * @throws IOException : Can't save the block to steam. -+ */ -+ public abstract void saveBlock(BufferedOutputStream stream) throws IOException; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (working copy) -@@ -0,0 +1,252 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockComplex(ByteBuffer bb, GeoFormat format) -+ { -+ // initialize buffer -+ _buffer = new byte[GeoStructure.BLOCK_CELLS * 3]; -+ -+ // load data -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // 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); -+ } -+ else -+ { -+ // get nswe -+ final byte nswe = bb.get(); -+ _buffer[i * 3] = nswe; -+ -+ // get height -+ final short height = bb.getShort(); -+ _buffer[(i * 3) + 1] = (byte) (height & 0x00FF); -+ _buffer[(i * 3) + 2] = (byte) (height >> 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height > worldZ ? height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height < worldZ ? height : Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public 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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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 int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_COMPLEX_L2D); -+ -+ // write block data -+ stream.write(_buffer, 0, GeoStructure.BLOCK_CELLS * 3); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (working copy) -@@ -0,0 +1,176 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockFlat extends ABlock -+{ -+ protected final short _height; -+ protected byte _nswe; -+ -+ /** -+ * Creates FlatBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockFlat(ByteBuffer bb, GeoFormat format) -+ { -+ _height = bb.getShort(); -+ _nswe = format != GeoFormat.L2D ? 0x0F : (byte) (0xFF); -+ if (format == GeoFormat.L2OFF) -+ { -+ bb.getShort(); -+ } -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return true; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height > worldZ ? _height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height < worldZ ? _height : Short.MAX_VALUE; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height > worldZ ? _nswe : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height < worldZ ? _nswe : 0; -+ } -+ -+ @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return index -+ return _height < worldZ ? 0 : -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _nswe = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_FLAT_L2D); -+ -+ // write height -+ stream.write((byte) (_height & 0x00FF)); -+ stream.write((byte) (_height >> 8)); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (working copy) -@@ -0,0 +1,465 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+import java.nio.ByteOrder; -+import java.util.Arrays; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockMultilayer extends ABlock -+{ -+ private static final int MAX_LAYERS = Byte.MAX_VALUE; -+ -+ private static ByteBuffer _temp; -+ -+ /** -+ * Initializes the temporarily buffer. -+ */ -+ public static void initialize() -+ { -+ // initialize temporarily buffer and sorting mechanism -+ _temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3); -+ _temp.order(ByteOrder.LITTLE_ENDIAN); -+ } -+ -+ /** -+ * Releases temporarily buffer. -+ */ -+ public static void release() -+ { -+ _temp = null; -+ } -+ -+ protected byte[] _buffer; -+ -+ /** -+ * Implicit constructor for children class. -+ */ -+ protected BlockMultilayer() -+ { -+ _buffer = null; -+ } -+ -+ /** -+ * Creates MultilayerBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockMultilayer(ByteBuffer bb, GeoFormat format) -+ { -+ // 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 = format != GeoFormat.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++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // get data -+ final short data = bb.getShort(); -+ -+ // add nswe and height -+ _temp.put((byte) (data & 0x000F)); -+ _temp.putShort((short) ((short) (data & 0xFFF0) >> 1)); -+ } -+ else -+ { -+ // add nswe -+ _temp.put(bb.get()); -+ -+ // add height -+ _temp.putShort(bb.getShort()); -+ } -+ } -+ } -+ -+ // 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 height -+ if (height > worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, return minimum value -+ return Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 height -+ if (height < worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, return maximum value -+ return Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 nswe -+ if (height > worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 nswe -+ if (height < worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @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 first layer data (first from bottom) -+ byte layers = _buffer[index++]; -+ -+ // loop though all cell layers, find closest layer -+ 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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ // get height -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(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]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ // get nswe -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ // set nswe -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_MULTILAYER_L2D); -+ -+ // for each cell -+ int index = 0; -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ // write layers count -+ final byte layers = _buffer[index++]; -+ stream.write(layers); -+ -+ // write cell data -+ stream.write(_buffer, index, layers * 3); -+ -+ // move index to next cell -+ index += layers * 3; -+ } -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (working copy) -@@ -0,0 +1,150 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockNull extends ABlock -+{ -+ private final byte _nswe; -+ -+ public BlockNull() -+ { -+ _nswe = (byte) 0xFF; -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return false; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweBelow(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) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) -+ { -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (nonexistent) -@@ -1,48 +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 . -- */ --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() -- { -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (nonexistent) -@@ -1,77 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (nonexistent) -@@ -1,56 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (working copy) -@@ -0,0 +1,39 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public enum GeoFormat -+{ -+ L2J("%d_%d.l2j"), -+ L2OFF("%d_%d_conv.dat"), -+ L2D("%d_%d.l2d"); -+ -+ private final String _filename; -+ -+ private GeoFormat(String filename) -+ { -+ _filename = filename; -+ } -+ -+ public String getFilename() -+ { -+ return _filename; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (working copy) -@@ -0,0 +1,67 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.geoengine.GeoEngine; -+import org.l2jmobius.gameserver.model.Location; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoLocation extends Location -+{ -+ private byte _nswe; -+ -+ public GeoLocation(int x, int y, int z) -+ { -+ super(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public void set(int x, int y, short z) -+ { -+ super.setXYZ(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public int getGeoX() -+ { -+ return _x; -+ } -+ -+ public int getGeoY() -+ { -+ return _y; -+ } -+ -+ @Override -+ public int getX() -+ { -+ return GeoEngine.getWorldX(_x); -+ } -+ -+ @Override -+ public int getY() -+ { -+ return GeoEngine.getWorldY(_y); -+ } -+ -+ public byte getNSWE() -+ { -+ return _nswe; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (working copy) -@@ -0,0 +1,70 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoStructure -+{ -+ // cells -+ public static final byte CELL_FLAG_E = 1 << 0; -+ public static final byte CELL_FLAG_W = 1 << 1; -+ public static final byte CELL_FLAG_S = 1 << 2; -+ public static final byte CELL_FLAG_N = 1 << 3; -+ public static final byte CELL_FLAG_SE = 1 << 4; -+ public static final byte CELL_FLAG_SW = 1 << 5; -+ public static final byte CELL_FLAG_NE = 1 << 6; -+ public static final byte CELL_FLAG_NW = (byte) (1 << 7); -+ -+ public static final int CELL_HEIGHT = 8; -+ public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6; -+ -+ // blocks -+ public static final byte TYPE_FLAT_L2J_L2OFF = 0; -+ public static final byte TYPE_FLAT_L2D = (byte) 0xD0; -+ public static final byte TYPE_COMPLEX_L2J = 1; -+ public static final byte TYPE_COMPLEX_L2OFF = 0x40; -+ public static final byte TYPE_COMPLEX_L2D = (byte) 0xD1; -+ 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) -+ public static final byte TYPE_MULTILAYER_L2D = (byte) 0xD2; -+ -+ 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; -+ -+ // regions -+ 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; -+ -+ // global geodata -+ private static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); -+ private 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; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (nonexistent) -@@ -1,42 +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 . -- */ --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); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (working copy) -@@ -0,0 +1,53 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public interface IGeoObject -+{ -+ /** -+ * Returns geodata X coordinate of the {@link IGeoObject}. -+ * @return int : Geodata X coordinate. -+ */ -+ int getGeoX(); -+ -+ /** -+ * Returns geodata Y coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Y coordinate. -+ */ -+ int getGeoY(); -+ -+ /** -+ * Returns geodata Z coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Z coordinate. -+ */ -+ int getGeoZ(); -+ -+ /** -+ * Returns height of the {@link IGeoObject}. -+ * @return int : Height. -+ */ -+ int getHeight(); -+ -+ /** -+ * Returns {@link IGeoObject} data. -+ * @return byte[][] : {@link IGeoObject} data. -+ */ -+ byte[][] getObjectGeoData(); -+} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (nonexistent) -@@ -1,47 +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 . -- */ --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(); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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); -- -- // 1 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (nonexistent) -@@ -1,55 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --/** -- * @author HorridoJoho -- */ --public final class NullRegion implements IRegion --{ -- public static final NullRegion INSTANCE = new NullRegion(); -- -- @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 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Region.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (nonexistent) -@@ -1,92 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (nonexistent) -@@ -1,87 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public abstract class AbstractNode --{ -- private T _loc; -- private AbstractNode _parent; -- -- public AbstractNode(T loc) -- { -- _loc = loc; -- } -- -- public void setParent(AbstractNode p) -- { -- _parent = p; -- } -- -- public AbstractNode getParent() -- { -- return _parent; -- } -- -- public T getLoc() -- { -- return _loc; -- } -- -- public void setLoc(T l) -- { -- _loc = l; -- } -- -- @Override -- public int hashCode() -- { -- final int prime = 31; -- int result = 1; -- result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); -- return result; -- } -- -- @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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (nonexistent) -@@ -1,33 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --/** -- * @author -Nemesiss- -- */ --public abstract class AbstractNodeLoc --{ -- public abstract int getX(); -- -- public abstract int getY(); -- -- public abstract int getZ(); -- -- public abstract int getNodeX(); -- -- public abstract int getNodeY(); --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (nonexistent) -@@ -1,67 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public class CellNode extends AbstractNode --{ -- 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (nonexistent) -@@ -1,348 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --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 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) -- { -- _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(); -- } -- -- public final List debugPath() -- { -- final List 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 void getNeighbors() -- { -- if (!_current.getLoc().canGoAll()) -- { -- 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); -- } -- -- // SouthEast -- if ((nodeE != null) && (nodeS != null)) -- { -- if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) -- { -- addNode(x + 1, y + 1, z, true); -- } -- } -- -- // SouthWest -- if ((nodeS != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) -- { -- addNode(x - 1, y + 1, z, true); -- } -- } -- -- // NorthEast -- if ((nodeN != null) && (nodeE != null)) -- { -- if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) -- { -- addNode(x + 1, y - 1, z, true); -- } -- } -- -- // NorthWest -- if ((nodeN != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) -- { -- addNode(x - 1, y - 1, z, true); -- } -- } -- } -- -- private 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(); -- // reinit node if needed -- if (result.getLoc() != null) -- { -- result.getLoc().set(x, y, z); -- } -- else -- { -- result.setLoc(new NodeLoc(x, y, z)); -- } -- } -- -- return result; -- } -- -- private 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 boolean isHighWeight(int x, int y, int z) -- { -- final CellNode result = getNode(x, y, z); -- if (result == null) -- { -- return true; -- } -- -- if (!result.getLoc().canGoAll()) -- { -- return true; -- } -- if (Math.abs(result.getLoc().getZ() - z) > 16) -- { -- return true; -- } -- -- return false; -- } -- -- private 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (working copy) -@@ -0,0 +1,87 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+ -+/** -+ * @author Hasha -+ */ -+public class Node -+{ -+ // node coords and nswe flag -+ private GeoLocation _loc; -+ -+ // node parent (for reverse path construction) -+ private Node _parent; -+ // node child (for moving over nodes during iteration) -+ private Node _child; -+ -+ // node G cost (movement cost = parent movement cost + current movement cost) -+ private double _cost = -1000; -+ -+ public void setLoc(int x, int y, int z) -+ { -+ _loc = new GeoLocation(x, y, z); -+ } -+ -+ public GeoLocation getLoc() -+ { -+ return _loc; -+ } -+ -+ public void setParent(Node parent) -+ { -+ _parent = parent; -+ } -+ -+ public Node getParent() -+ { -+ return _parent; -+ } -+ -+ public void setChild(Node child) -+ { -+ _child = child; -+ } -+ -+ public Node getChild() -+ { -+ return _child; -+ } -+ -+ public void setCost(double cost) -+ { -+ _cost = cost; -+ } -+ -+ public double getCost() -+ { -+ return _cost; -+ } -+ -+ public void free() -+ { -+ // reset node location -+ _loc = null; -+ -+ // reset node parent, child and cost -+ _parent = null; -+ _child = null; -+ _cost = -1000; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (working copy) -@@ -0,0 +1,306 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import java.util.concurrent.locks.ReentrantLock; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+ -+/** -+ * @author DS, Hasha; Credits to Diamond -+ */ -+public class NodeBuffer -+{ -+ private final ReentrantLock _lock = new ReentrantLock(); -+ private final int _size; -+ private final Node[][] _buffer; -+ -+ // center coordinates -+ private int _cx = 0; -+ private int _cy = 0; -+ -+ // target coordinates -+ private int _gtx = 0; -+ private int _gty = 0; -+ private short _gtz = 0; -+ -+ private Node _current = null; -+ -+ /** -+ * Constructor of NodeBuffer. -+ * @param size : one dimension size of buffer -+ */ -+ public NodeBuffer(int size) -+ { -+ // set size -+ _size = size; -+ -+ // initialize buffer -+ _buffer = new Node[size][size]; -+ for (int x = 0; x < size; x++) -+ { -+ for (int y = 0; y < size; y++) -+ { -+ _buffer[x][y] = 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 Node : first node of path -+ */ -+ public Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) -+ { -+ // set coordinates (middle of the line (gox,goy) - (gtx,gty), will be in the center of the buffer) -+ _cx = gox + ((gtx - gox - _size) / 2); -+ _cy = goy + ((gty - goy - _size) / 2); -+ -+ _gtx = gtx; -+ _gty = gty; -+ _gtz = gtz; -+ -+ _current = getNode(gox, goy, goz); -+ _current.setCost(getCostH(gox, goy, goz)); -+ -+ int count = 0; -+ do -+ { -+ // reached target? -+ if ((_current.getLoc().getGeoX() == _gtx) && (_current.getLoc().getGeoY() == _gty) && (Math.abs(_current.getLoc().getZ() - _gtz) < 8)) -+ { -+ return _current; -+ } -+ -+ // expand current node -+ expand(); -+ -+ // move pointer -+ _current = _current.getChild(); -+ } -+ while ((_current != null) && (count++ < Config.MAX_ITERATIONS)); -+ -+ return null; -+ } -+ -+ public boolean isLocked() -+ { -+ return _lock.tryLock(); -+ } -+ -+ public void free() -+ { -+ _current = null; -+ -+ for (Node[] nodes : _buffer) -+ { -+ for (Node node : nodes) -+ { -+ if (node.getLoc() != null) -+ { -+ node.free(); -+ } -+ } -+ } -+ -+ _lock.unlock(); -+ } -+ -+ /** -+ * Check _current Node and add its neighbors to the buffer. -+ */ -+ private final void expand() -+ { -+ // can't move anywhere, don't expand -+ final byte nswe = _current.getLoc().getNSWE(); -+ if (nswe == 0) -+ { -+ return; -+ } -+ -+ // get geo coords of the node to be expanded -+ final int x = _current.getLoc().getGeoX(); -+ final int y = _current.getLoc().getGeoY(); -+ final short z = (short) _current.getLoc().getZ(); -+ -+ // can move north, expand -+ if ((nswe & GeoStructure.CELL_FLAG_N) != 0) -+ { -+ addNode(x, y - 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move south, expand -+ if ((nswe & GeoStructure.CELL_FLAG_S) != 0) -+ { -+ addNode(x, y + 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_W) != 0) -+ { -+ addNode(x - 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_E) != 0) -+ { -+ addNode(x + 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move north-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NW) != 0) -+ { -+ addNode(x - 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move north-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NE) != 0) -+ { -+ addNode(x + 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SW) != 0) -+ { -+ addNode(x - 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SE) != 0) -+ { -+ addNode(x + 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ } -+ -+ /** -+ * Returns node, if it exists in buffer. -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param z : node Z coord -+ * @return Node : node, if exits in buffer -+ */ -+ private final Node getNode(int x, int y, short z) -+ { -+ // check node X out of coordinates -+ final int ix = x - _cx; -+ if ((ix < 0) || (ix >= _size)) -+ { -+ return null; -+ } -+ -+ // check node Y out of coordinates -+ final int iy = y - _cy; -+ if ((iy < 0) || (iy >= _size)) -+ { -+ return null; -+ } -+ -+ // get node -+ final Node result = _buffer[ix][iy]; -+ -+ // check and update -+ if (result.getLoc() == null) -+ { -+ result.setLoc(x, y, z); -+ } -+ -+ // return node -+ return result; -+ } -+ -+ /** -+ * Add node given by coordinates to the buffer. -+ * @param x : geo X coord -+ * @param y : geo Y coord -+ * @param z : geo Z coord -+ * @param weight : weight of movement to new node -+ */ -+ private final void addNode(int x, int y, short z, int weight) -+ { -+ // get node to be expanded -+ final Node node = getNode(x, y, z); -+ if (node == null) -+ { -+ return; -+ } -+ -+ // Z distance between nearby cells is higher than cell size -+ if (node.getLoc().getZ() > (z + (2 * GeoStructure.CELL_HEIGHT))) -+ { -+ return; -+ } -+ -+ // node was already expanded, return -+ if (node.getCost() >= 0) -+ { -+ return; -+ } -+ -+ node.setParent(_current); -+ if (node.getLoc().getNSWE() != (byte) 0xFF) -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + (weight * Config.OBSTACLE_MULTIPLIER)); -+ } -+ else -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + weight); -+ } -+ -+ Node current = _current; -+ int count = 0; -+ while ((current.getChild() != null) && (count < (Config.MAX_ITERATIONS * 4))) -+ { -+ count++; -+ if (current.getChild().getCost() > node.getCost()) -+ { -+ node.setChild(current.getChild()); -+ break; -+ } -+ current = current.getChild(); -+ } -+ -+ if (count >= (Config.MAX_ITERATIONS * 4)) -+ { -+ System.err.println("Pathfinding: too long loop detected, cost:" + node.getCost()); -+ } -+ -+ current.setChild(node); -+ } -+ -+ /** -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param i : node Z coord -+ * @return double : node cost -+ */ -+ private final double getCostH(int x, int y, int i) -+ { -+ final int dX = x - _gtx; -+ final int dY = y - _gty; -+ final int dZ = (i - _gtz) / GeoStructure.CELL_HEIGHT; -+ -+ // return (Math.abs(dX) + Math.abs(dY) + Math.abs(dZ)) * Config.HEURISTIC_WEIGHT; // Manhattan distance -+ return Math.sqrt((dX * dX) + (dY * dY) + (dZ * dZ)) * Config.HEURISTIC_WEIGHT; // Direct distance -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -- --/** -- * @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 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 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; -- // return super.hashCode(); -- } -- -- @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; -- if (_x != other._x) -- { -- return false; -- } -- if (_y != other._y) -- { -- return false; -- } -- if (_goNorth != other._goNorth) -- { -- return false; -- } -- if (_goEast != other._goEast) -- { -- return false; -- } -- if (_goSouth != other._goSouth) -- { -- return false; -- } -- if (_goWest != other._goWest) -- { -- return false; -- } -- if (_geoHeight != other._geoHeight) -- { -- return false; -- } -- return true; -- } --} -Index: java/org/l2jmobius/gameserver/model/actor/Creature.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/Creature.java (revision 8428) -+++ java/org/l2jmobius/gameserver/model/actor/Creature.java (working copy) -@@ -65,8 +65,6 @@ - import org.l2jmobius.gameserver.enums.TeleportWhereType; - import org.l2jmobius.gameserver.enums.UserInfoType; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; - import org.l2jmobius.gameserver.instancemanager.IdManager; - import org.l2jmobius.gameserver.instancemanager.MapRegionManager; - import org.l2jmobius.gameserver.instancemanager.QuestManager; -@@ -2566,7 +2564,7 @@ - - public boolean disregardingGeodata; - public int onGeodataPathIndex; -- public List geoPath; -+ public List geoPath; - public int geoPathAccurateTx; - public int geoPathAccurateTy; - public int geoPathGtx; -@@ -3443,7 +3441,7 @@ - if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) - { - // Path calculation -- overrides previous movement check -- m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); -+ m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); - if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found - { - if (isPlayer() && !_isFlying && !isInWater) -Index: java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (revision 8424) -+++ java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (working copy) -@@ -12746,7 +12746,7 @@ - { - if (Config.CORRECT_PLAYER_Z) - { -- final int nearestZ = GeoEngine.getInstance().getHigherHeight(getX(), getY(), getZ()); -+ final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); - if (getZ() < nearestZ) - { - teleToLocation(new Location(getX(), getY(), nearestZ)); -Index: java/org/l2jmobius/gameserver/util/GeoUtils.java -=================================================================== ---- java/org/l2jmobius/gameserver/util/GeoUtils.java (revision 8409) -+++ java/org/l2jmobius/gameserver/util/GeoUtils.java (working copy) -@@ -19,7 +19,7 @@ - import java.awt.Color; - - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; - import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; - -@@ -26,25 +26,25 @@ - /** - * @author HorridoJoho - */ --public final class GeoUtils -+public class GeoUtils - { - public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); - - final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); - - while (iter.next()) - { -- final int wx = GeoEngine.getInstance().getWorldX(iter.x()); -- final int wy = GeoEngine.getInstance().getWorldY(iter.y()); -+ final int wx = GeoEngine.getWorldX(iter.x()); -+ final int wy = GeoEngine.getWorldY(iter.y()); - - prim.addPoint(Color.RED, wx, wy, z); - } -@@ -53,21 +53,21 @@ - - public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); -- int wy = GeoEngine.getInstance().getWorldY(prevY); -+ int wx = GeoEngine.getWorldX(prevX); -+ int wy = GeoEngine.getWorldY(prevY); - int wz = iter.z(); - prim.addPoint(Color.RED, wx, wy, wz); - -@@ -78,8 +78,8 @@ - - if ((curX != prevX) || (curY != prevY)) - { -- wx = GeoEngine.getInstance().getWorldX(curX); -- wy = GeoEngine.getInstance().getWorldY(curY); -+ wx = GeoEngine.getWorldX(curX); -+ wy = GeoEngine.getWorldY(curY); - wz = iter.z(); - - prim.addPoint(Color.RED, wx, wy, wz); -@@ -93,7 +93,7 @@ - - private static Color getDirectionColor(int x, int y, int z, int nswe) - { -- if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) -+ if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) - { - return Color.GREEN; - } -@@ -109,9 +109,8 @@ - int iPacket = 0; - - ExServerPrimitive exsp = null; -- final GeoEngine ge = GeoEngine.getInstance(); -- final int playerGx = ge.getGeoX(player.getX()); -- final int playerGy = ge.getGeoY(player.getY()); -+ final int playerGx = GeoEngine.getGeoX(player.getX()); -+ final int playerGy = GeoEngine.getGeoY(player.getY()); - for (int dx = -geoRadius; dx <= geoRadius; ++dx) - { - for (int dy = -geoRadius; dy <= geoRadius; ++dy) -@@ -135,12 +134,12 @@ - final int gx = playerGx + dx; - final int gy = playerGy + dy; - -- final int x = ge.getWorldX(gx); -- final int y = ge.getWorldY(gy); -- final int z = ge.getNearestZ(gx, gy, player.getZ()); -+ final int x = GeoEngine.getWorldX(gx); -+ final int y = GeoEngine.getWorldY(gy); -+ final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); - - // north arrow -- Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); -+ Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); - 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); -@@ -147,7 +146,7 @@ - exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z); - - // east arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); - 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); -@@ -154,13 +153,13 @@ - exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z); - - // south arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); - 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, Cell.NSWE_WEST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); - 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); -@@ -188,15 +187,15 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_EAST; -+ return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_EAST; -+ return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; - } - else - { -- return Cell.NSWE_EAST; -+ return GeoStructure.CELL_FLAG_E; // Direction.EAST; - } - } - else if (x < lastX) // west -@@ -203,26 +202,27 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_WEST; -+ return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_WEST; -+ return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; - } - else - { -- return Cell.NSWE_WEST; -+ return GeoStructure.CELL_FLAG_W; // Direction.WEST; - } - } -- else // unchanged x -+ else -+ // unchanged x - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH; -+ return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH; -+ return GeoStructure.CELL_FLAG_N; // Direction.NORTH; - } - else - { -Index: java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java -=================================================================== ---- java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (nonexistent) -+++ java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (working copy) -@@ -0,0 +1,386 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.tools.geodataconverter; -+ -+import java.io.BufferedOutputStream; -+import java.io.File; -+import java.io.FileOutputStream; -+import java.io.RandomAccessFile; -+import java.nio.ByteOrder; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.Scanner; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.commons.util.PropertiesParser; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoDataConverter -+{ -+ private static GeoFormat _format; -+ private static ABlock[][] _blocks; -+ -+ public static void main(String[] args) -+ { -+ // initialize config -+ loadGeoengineConfigs(); -+ -+ // get geodata format -+ String type = ""; -+ try (Scanner scn = new Scanner(System.in)) -+ { -+ while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) -+ { -+ System.out.println("GeoDataConverter: Select source geodata type:"); -+ System.out.println(" J: L2J (e.g. 23_22.l2j)"); -+ System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); -+ System.out.println(" E: Exit"); -+ System.out.print("Choice: "); -+ type = scn.next(); -+ } -+ } -+ if (type.equalsIgnoreCase("E")) -+ { -+ System.exit(0); -+ } -+ -+ _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; -+ -+ // start conversion -+ System.out.println("GeoDataConverter: Converting all " + _format + " files."); -+ -+ // initialize geodata container -+ _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files -+ int converted = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) -+ { -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) -+ { -+ final String input = String.format(_format.getFilename(), rx, ry); -+ final String filepath = Config.GEODATA_PATH; -+ final File f = new File(filepath + input); -+ if (f.exists() && !f.isDirectory()) -+ { -+ // load geodata -+ if (!loadGeoBlocks(input)) -+ { -+ System.out.println("GeoDataConverter: Unable to load " + input + " region file."); -+ continue; -+ } -+ -+ // recalculate nswe -+ if (!recalculateNswe()) -+ { -+ System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); -+ continue; -+ } -+ -+ // save geodata -+ final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); -+ if (!saveGeoBlocks(output)) -+ { -+ System.out.println("GeoDataConverter: Unable to save " + output + " region file."); -+ continue; -+ } -+ -+ converted++; -+ System.out.println("GeoDataConverter: Created " + output + " region file."); -+ } -+ } -+ } -+ System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); -+ } -+ -+ /** -+ * Loads geo blocks from buffer of the region file. -+ * @param filename : The name of the to load. -+ * @return boolean : True when successful. -+ */ -+ private static boolean loadGeoBlocks(String filename) -+ { -+ // region file is load-able, try to load it -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) -+ { -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) -+ if (_format == GeoFormat.L2OFF) -+ { -+ for (int i = 0; i < 18; i++) -+ { -+ buffer.get(); -+ } -+ } -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ if (_format == GeoFormat.L2J) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2J: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2J: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ else -+ { -+ // get block type -+ final short type = buffer.getShort(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ default: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ if (buffer.remaining() > 0) -+ { -+ System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ return false; -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); -+ return false; -+ } -+ } -+ -+ /** -+ * Recalculate diagonal flags for the region file. -+ * @return boolean : True when successful. -+ */ -+ private static boolean recalculateNswe() -+ { -+ try -+ { -+ for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) -+ { -+ for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) -+ { -+ // get block -+ final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // skip flat blocks -+ if (block instanceof BlockFlat) -+ { -+ continue; -+ } -+ -+ // for complex and multilayer blocks go though all layers -+ short height = Short.MAX_VALUE; -+ int index; -+ while ((index = block.getIndexBelow(x, y, height)) != -1) -+ { -+ // get height and nswe -+ height = block.getHeight(index); -+ byte nswe = block.getNswe(index); -+ -+ // update nswe with diagonal flags -+ nswe = updateNsweBelow(x, y, height, nswe); -+ -+ // set nswe of the cell -+ block.setNswe(index, nswe); -+ } -+ } -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ /** -+ * Updates the NSWE flag with diagonal flags. -+ * @param x : Geodata X coordinate. -+ * @param y : Geodata Y coordinate. -+ * @param z : Geodata Z coordinate. -+ * @param nsweValue : NSWE flag to be updated. -+ * @return byte : Updated NSWE flag. -+ */ -+ private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) -+ { -+ byte nswe = nsweValue; -+ -+ // calculate virtual layer height -+ final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); -+ -+ // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) -+ final byte nsweN = getNsweBelow(x, y - 1, height); -+ final byte nsweS = getNsweBelow(x, y + 1, height); -+ final byte nsweW = getNsweBelow(x - 1, y, height); -+ final byte nsweE = getNsweBelow(x + 1, y, height); -+ -+ // north-west -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NW; -+ } -+ -+ // north-east -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NE; -+ } -+ -+ // south-west -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SW; -+ } -+ -+ // south-east -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SE; -+ } -+ -+ return nswe; -+ } -+ -+ private static byte getNsweBelow(int geoX, int geoY, short worldZ) -+ { -+ // out of geo coordinates -+ if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) -+ { -+ return 0; -+ } -+ -+ // out of geo coordinates -+ if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) -+ { -+ return 0; -+ } -+ -+ // get block -+ final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // get index, when valid, return nswe -+ final int index = block.getIndexBelow(geoX, geoY, worldZ); -+ return index == -1 ? 0 : block.getNswe(index); -+ } -+ -+ /** -+ * Save region file to file. -+ * @param filename : The name of file to save. -+ * @return boolean : True when successful. -+ */ -+ private static boolean saveGeoBlocks(String filename) -+ { -+ try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) -+ { -+ // loop over region blocks and save each block -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[ix][iy].saveBlock(bos); -+ } -+ } -+ -+ // flush data to file -+ bos.flush(); -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ private static void loadGeoengineConfigs() -+ { -+ final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); -+ Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); -+ Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); -+ Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); -+ Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); -+ Config.PATHFINDING = geoData.getBoolean("PathFinding", true); -+ Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -+ Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); -+ Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); -+ Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); -+ Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); -+ Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); -+ } -+} -\ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/geodata/Readme.txt b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/geodata/Readme.txt index a480c8c380..2611882e56 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/geodata/Readme.txt +++ b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/geodata/Readme.txt @@ -23,8 +23,8 @@ a - Prerequisites b - Make it work ---------------------------------------------- -To make geodata working: -* unpack your geodata files into "/data/geodata" folder -* open "/config/GeoEngine.ini" with your favorite text editor and then edit following config: - - CoordSynchronize = 2 -* If you do not use any geodata files, the server will automatically change this setting to -1. +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. diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/ai/others/FleeMonsters.java b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/ai/others/FleeMonsters.java index 4562336103..a971742033 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/ai/others/FleeMonsters.java +++ b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/ai/others/FleeMonsters.java @@ -59,7 +59,7 @@ public class FleeMonsters extends AbstractNpcAI final int posX = (int) (npc.getX() + (FLEE_DISTANCE * Math.cos(radians))); final int posY = (int) (npc.getY() + (FLEE_DISTANCE * Math.sin(radians))); final int posZ = npc.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, attacker.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); return super.onAttack(npc, attacker, damage, isSummon); } diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/ai/others/RandomWalkingGuards.java b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/ai/others/RandomWalkingGuards.java index b99becfdc6..780502d5d6 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/ai/others/RandomWalkingGuards.java +++ b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/ai/others/RandomWalkingGuards.java @@ -55,7 +55,7 @@ public class RandomWalkingGuards extends AbstractNpcAI if (!npc.isInCombat()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, Config.MAX_DRIFT_RANGE); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("RANDOM_WALK", getRandom(MIN_WALK_DELAY, MAX_WALK_DELAY), npc, null); } diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index 524652cbc1..18511c6541 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { @@ -133,8 +133,8 @@ public class AdminGeodata implements IAdminCommandHandler } case "admin_geomap": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y + " (" + ((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + "," + ((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + " to " + ((((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + "," + ((((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + ")"); break; } diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java index f3dffa1227..76572f7f25 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java +++ b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java @@ -57,8 +57,8 @@ public class AdminMissingHtmls implements IAdminCommandHandler { case "admin_geomap_missing_htmls": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final int topLeftX = (x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE; final int topLeftY = (y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE; final int bottomRightX = (((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1; diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index fdb2319138..a0a34f1051 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -19,9 +19,9 @@ package handlers.admincommandhandlers; import java.util.List; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; +import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.util.BuilderUtil; @@ -44,13 +44,13 @@ public class AdminPathNode implements IAdminCommandHandler } if (activeChar.getTarget() != null) { - final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { BuilderUtil.sendSysMessage(activeChar, "No Route!"); return true; } - for (AbstractNodeLoc a : path) + for (Location a : path) { BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/Blink.java b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/Blink.java index 22f2c84eae..22697e8e20 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/Blink.java +++ b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/Blink.java @@ -88,7 +88,7 @@ public class Blink extends AbstractEffect final int y = effected.getY() + y1; final int z = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, destination, _flyType, _flySpeed, _flyDelay, _animationSpeed)); diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/Fear.java b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/Fear.java index 95a5afdf98..88c795e04b 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/Fear.java +++ b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/Fear.java @@ -100,7 +100,7 @@ public class Fear extends AbstractEffect final int posY = (int) (effected.getY() + (FEAR_RANGE * Math.sin(radians))); final int posZ = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); } } diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java index fddd94c60a..55c2e78d8a 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java +++ b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java @@ -57,7 +57,7 @@ public class FlyAway extends AbstractEffect final int y = (int) (effector.getY() - (nRadius * (dy / distance))); final int z = effector.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.broadcastPacket(new FlyToLocation(effected, destination, FlyType.THROW_UP)); effected.setXYZ(destination); diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java index 1fbd8239b0..89ad8670dc 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java +++ b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java @@ -133,7 +133,7 @@ public class KnockBack extends AbstractEffect final int x = (int) (effected.getX() + (_distance * Math.cos(radians))); final int y = (int) (effected.getY() + (_distance * Math.sin(radians))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, loc, _type, _speed, _delay, _animationSpeed)); diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/PullBack.java b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/PullBack.java index ed6a37eff6..38fe2fe9d0 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/PullBack.java +++ b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/PullBack.java @@ -73,7 +73,7 @@ public class PullBack extends AbstractEffect } // In retail, you get debuff, but you are not even moved if there is obstacle. You are still disabled from using skills and moving though. - if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effector.getInstanceWorld())) + if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effected.getInstanceWorld())) { effected.broadcastPacket(new FlyToLocation(effected, effector, _type, _speed, _delay, _animationSpeed)); effected.setXYZ(effector.getX(), effector.getY(), GeoEngine.getInstance().getHeight(effector.getX(), effector.getY(), effector.getZ()) + 10); diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java index d6f9664141..813e496c47 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java +++ b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java @@ -87,7 +87,7 @@ public class TeleportToSummon extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = summon.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java index d3c263978c..c9dc34ac22 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java +++ b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java @@ -76,7 +76,7 @@ public class TeleportToTarget extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java index f2148adb54..c7ca6f64cb 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java +++ b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java @@ -65,7 +65,7 @@ public class RollingDice implements IItemHandler final int x = player.getX() + x1; final int y = player.getY() + y1; final int z = player.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); Broadcast.toSelfAndKnownPlayers(player, new Dice(player.getObjectId(), itemId, number, destination.getX(), destination.getY(), destination.getZ())); final SystemMessage sm = new SystemMessage(SystemMessageId.C1_HAS_ROLLED_A_S2); diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/targethandlers/Ground.java b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/targethandlers/Ground.java index 328c9fb38a..2d4b94e385 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/targethandlers/Ground.java +++ b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/handlers/targethandlers/Ground.java @@ -52,7 +52,7 @@ public class Ground implements ITargetTypeHandler return null; } - if (!GeoEngine.getInstance().canSeeTarget(creature, worldPosition)) + if (!GeoEngine.getInstance().canSeeLocation(creature, worldPosition)) { if (sendMessage) { diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/Config.java index 759f0e6ecc..2a74d75080 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/Config.java @@ -61,6 +61,7 @@ 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; @@ -889,12 +890,17 @@ public class Config // GeoEngine // -------------------------------------------------- public static Path GEODATA_PATH; + public static GeoType GEODATA_TYPE; public static boolean PATHFINDING; public static String PATHFIND_BUFFERS; - public static float LOW_WEIGHT; - public static float MEDIUM_WEIGHT; - public static float HIGH_WEIGHT; - public static float DIAGONAL_WEIGHT; + public static int MOVE_WEIGHT; + public static int MOVE_WEIGHT_DIAG; + public static int OBSTACLE_WEIGHT; + 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; /** Attribute System */ public static int S_WEAPON_STONE; @@ -2386,12 +2392,17 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); + GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); - MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); - HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); - DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); + MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); + OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); + HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); + MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); + MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); // Load AllowedPlayerRaces config file (if exists) final PropertiesParser AllowedPlayerRaces = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE); diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/commons/util/CommonUtil.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/commons/util/CommonUtil.java index 14ae3a4560..ab7837d827 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/commons/util/CommonUtil.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/commons/util/CommonUtil.java @@ -584,4 +584,15 @@ public class CommonUtil final DecimalFormat formatter = new DecimalFormat(format, new DecimalFormatSymbols(Locale.ENGLISH)); return formatter.format(value); } + + /** + * @param numToTest : The number to test. + * @param min : The minimum limit. + * @param max : The maximum limit. + * @return the number or one of the limit (mininum / maximum). + */ + public static int limit(int numToTest, int min, int max) + { + return (numToTest > max) ? max : ((numToTest < min) ? min : numToTest); + } } diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/commons/util/Point2D.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/commons/util/Point2D.java new file mode 100644 index 0000000000..ebb6272e5e --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/commons/util/Point2D.java @@ -0,0 +1,180 @@ +/* + * 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 . + */ +package org.l2jmobius.commons.util; + +import java.util.Objects; + +/** + * A datatype used to retain a 2D (x/y) point. It got the capability to be set and cleaned. + */ +public class Point2D +{ + protected volatile int _x; + protected volatile int _y; + + public Point2D(int x, int y) + { + _x = x; + _y = y; + } + + @Override + public Point2D clone() + { + return new Point2D(_x, _y); + } + + @Override + public String toString() + { + return _x + ", " + _y; + } + + @Override + public int hashCode() + { + return Objects.hash(_x, _y); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final Point2D other = (Point2D) obj; + return (_x == other._x) && (_y == other._y); + } + + /** + * @param x : The X coord to test. + * @param y : The Y coord to test. + * @return True if all coordinates equals this {@link Point2D} coordinates. + */ + public boolean equals(int x, int y) + { + return (_x == x) && (_y == y); + } + + public int getX() + { + return _x; + } + + public void setX(int x) + { + _x = x; + } + + public int getY() + { + return _y; + } + + public void setY(int y) + { + _y = y; + } + + public void set(int x, int y) + { + _x = x; + _y = y; + } + + /** + * Refresh the current {@link Point2D} using a reference {@link Point2D} and a distance. The new destination is calculated to go in opposite side of the {@link Point2D} reference.
+ *
+ * This method is perfect to calculate fleeing characters position. + * @param referenceLoc : The Point2D used as position. + * @param distance : The distance to be set between current and new position. + */ + public void setFleeing(Point2D referenceLoc, int distance) + { + final double xDiff = referenceLoc.getX() - _x; + final double yDiff = referenceLoc.getY() - _y; + + final double yxRation = Math.abs(xDiff / yDiff); + + final int y = (int) (distance / (yxRation + 1)); + final int x = (int) (y * yxRation); + + _x += (xDiff < 0 ? x : -x); + _y += (yDiff < 0 ? y : -y); + } + + public void clean() + { + _x = 0; + _y = 0; + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @return The distance between this {@Point2D} and some given coordinates. + */ + public double distance2D(int x, int y) + { + final double dx = (double) _x - x; + final double dy = (double) _y - y; + + return Math.sqrt((dx * dx) + (dy * dy)); + } + + /** + * @param point : The {@link Point2D} to test. + * @return The distance between this {@Point2D} and the {@link Point2D} set as parameter. + */ + public double distance2D(Point2D point) + { + return distance2D(point.getX(), point.getY()); + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of some given coordinates. + */ + public boolean isIn2DRadius(int x, int y, int radius) + { + return distance2D(x, y) < radius; + } + + /** + * @param point : The Point2D to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of the {@link Point2D} set as parameter. + */ + public boolean isIn2DRadius(Point2D point, int radius) + { + return distance2D(point) < radius; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/AttackableAI.java index 547963e3e8..dc29d4cf6e 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/AttackableAI.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/ai/AttackableAI.java @@ -578,7 +578,7 @@ public class AttackableAI extends CreatureAI } // Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast) - final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); + final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); moveTo(moveLoc.getX(), moveLoc.getY(), moveLoc.getZ()); } } @@ -801,7 +801,7 @@ public class AttackableAI extends CreatureAI final int newZ = npc.getZ() + 30; // Mobius: Verify destination. Prevents wall collision issues and fixes monsters not avoiding obstacles. - moveTo(GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); + moveTo(GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); } return; } diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/data/SpawnTable.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/data/SpawnTable.java index a81201f69e..19f0db5b3e 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/data/SpawnTable.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/data/SpawnTable.java @@ -114,8 +114,8 @@ public class SpawnTable } // XML file for spawn - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); // Write info to XML @@ -192,8 +192,8 @@ public class SpawnTable if (update) { - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = spawn.getNpcSpawnTemplate() != null ? spawn.getNpcSpawnTemplate().getSpawnTemplate().getFile() : new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); final File tempFile = new File(spawnFile.getAbsolutePath().substring(Config.DATAPACK_ROOT.getAbsolutePath().length() + 1).replace('\\', '/') + ".tmp"); try diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/enums/GeoType.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/enums/GeoType.java new file mode 100644 index 0000000000..f77aa5eac4 --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/enums/GeoType.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.enums; + +public enum GeoType +{ + L2J("%d_%d.l2j"), + L2OFF("%d_%d_conv.dat"); + + private final String _filename; + + private GeoType(String filename) + { + _filename = filename; + } + + public String getFilename() + { + return _filename; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java new file mode 100644 index 0000000000..e62f50a8df --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java @@ -0,0 +1,144 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 9d255a5f54..7bef9c770b 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,72 +16,63 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.channels.FileChannel.MapMode; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.logging.Level; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; import java.util.logging.Logger; import org.l2jmobius.Config; +import org.l2jmobius.commons.util.CommonUtil; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; -import org.l2jmobius.gameserver.geoengine.geodata.IRegion; -import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; -import org.l2jmobius.gameserver.geoengine.geodata.Region; +import org.l2jmobius.gameserver.enums.GeoType; +import org.l2jmobius.gameserver.enums.MoveDirectionType; +import org.l2jmobius.gameserver.geoengine.geodata.ABlock; +import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; +import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; +import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; +import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.model.interfaces.ILocational; -import org.l2jmobius.gameserver.util.GeoUtils; -import org.l2jmobius.gameserver.util.LinePointIterator; -import org.l2jmobius.gameserver.util.LinePointIterator3D; -/** - * @author -Nemesiss-, HorridoJoho - */ public class GeoEngine { - private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - - private static final int WORLD_MIN_X = -655360; - private static final int WORLD_MIN_Y = -589824; - private static final int WORLD_MIN_Z = -16384; - - /** 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; - - /** The regions array */ - private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; - private static final int ELEVATED_SEE_OVER_DISTANCE = 2; - private static final int MAX_SEE_OVER_HEIGHT = 48; - protected GeoEngine() + private final ABlock[][] _blocks; + private final BlockNull _nullBlock; + + // Pre-allocated buffers. + private final BufferHolder[] _buffers; + + public GeoEngine() { LOGGER.info("GeoEngine: Initializing..."); - for (int i = 0; i < _regions.length(); i++) - { - _regions.set(i, NullRegion.INSTANCE); - } + // Initialize block container. + _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; + + // Load null block. + _nullBlock = new BlockNull(); + + // Initialize multilayer temporarily buffer. + BlockMultilayer.initialize(); + + // Load geo files according to geoengine config setup. int loaded = 0; try { @@ -92,23 +83,52 @@ public class GeoEngine final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); if (Files.exists(geoFilePath)) { - try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + // Region file is load-able, try to load it. + if (loadGeoBlocks(regionX, regionY)) { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); loaded++; } } + else + { + // Region file is not load-able, load null blocks. + loadNullBlocks(regionX, regionY); + } } } } catch (Exception e) { - LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + LOGGER.warning("GeoEngine: Failed to load geodata! " + e); System.exit(1); } - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + // Release multilayer block temporarily buffer. + BlockMultilayer.release(); + + String[] array = Config.PATHFIND_BUFFERS.split(";"); + _buffers = new BufferHolder[array.length]; + + int count = 0; + for (int i = 0; i < array.length; i++) + { + String buf = array[i]; + String[] args = buf.split("x"); + + try + { + int size = Integer.parseInt(args[1]); + count += size; + _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); + } + catch (Exception e) + { + LOGGER.warning("Could not load buffer setting:" + buf + ". " + e); + } + } + LOGGER.info("Loaded " + count + " node buffers."); + // Avoid wrong configs when no files are loaded. if ((loaded == 0) && Config.PATHFINDING) { @@ -118,616 +138,913 @@ public class GeoEngine } /** - * @param geoX - * @param geoY - * @return the region + * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer and log this situation. + * @param size : pre-calculated minimal required size + * @return NodeBuffer : buffer */ - private IRegion getRegion(int geoX, int geoY) + private NodeBuffer getBuffer(int size) { - final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); - if ((region < 0) || (region >= _regions.length())) + NodeBuffer current = null; + for (BufferHolder holder : _buffers) { - return null; - } - return _regions.get(region); - } - - /** - * @param filePath - * @param regionX - * @param regionY - * @throws IOException - */ - 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()).load().order(ByteOrder.LITTLE_ENDIAN))); - } - } - - /** - * @param regionX - * @param regionY - */ - public void unloadRegion(int regionX, int regionY) - { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); - } - - /** - * @param geoX - * @param geoY - * @return if geodata exist - */ - public boolean hasGeoPos(int geoX, int geoY) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return false; - } - return region.hasGeo(); - } - - /** - * Checks the specified position for available geodata. - * @param x the world x - * @param y the world y - * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise - */ - public boolean hasGeo(int x, int y) - { - return hasGeoPos(getGeoX(x), getGeoY(y)); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe check - */ - public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return true; - } - return region.checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe anti-corner cut - */ - public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) - { - boolean can = true; - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - return can && checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the nearest Z value - */ - public int getNearestZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNearestZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next lower Z value - */ - public int getNextLowerZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextLowerZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next higher Z value - */ - public int getNextHigherZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextHigherZ(geoX, geoY, worldZ); - } - - /** - * Gets the Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHeight(int x, int y, int z) - { - return getNearestZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next lower Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getLowerHeight(int x, int y, int z) - { - return getNextLowerZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next higher Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHigherHeight(int x, int y, int z) - { - return getNextHigherZ(getGeoX(x), getGeoY(y), z); - } - - /** - * @param worldX - * @return the geo X - */ - public int getGeoX(int worldX) - { - return (worldX - WORLD_MIN_X) / 16; - } - - /** - * @param worldY - * @return the geo Y - */ - public int getGeoY(int worldY) - { - return (worldY - WORLD_MIN_Y) / 16; - } - - /** - * @param worldZ - * @return the geo Z - */ - public int getGeoZ(int worldZ) - { - return (worldZ - WORLD_MIN_Z) / 16; - } - - /** - * @param geoX - * @return the world X - */ - public int getWorldX(int geoX) - { - return (geoX * 16) + WORLD_MIN_X + 8; - } - - /** - * @param geoY - * @return the world Y - */ - public int getWorldY(int geoY) - { - return (geoY * 16) + WORLD_MIN_Y + 8; - } - - /** - * @param geoZ - * @return the world Z - */ - public int getWorldZ(int geoZ) - { - return (geoZ * 16) + WORLD_MIN_Z + 8; - } - - /** - * Can see target. Doors as target always return true. Checks doors between. - * @param cha the character - * @param target the target - * @return {@code true} if the character can see the target (LOS), {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, WorldObject target) - { - if (target.isDoor()) - { - // Can always see doors. - return true; - } - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); - } - - /** - * Can see target. Checks doors between. - * @param cha the character - * @param worldPosition the world position - * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) - { - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param world - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @param tworld the target's instanceId - * @return - */ - public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) - { - return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instance - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) - { - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) - { - return false; - } - return canSeeTarget(x, y, z, tx, ty, tz); - } - - /** - * @param prevX - * @param prevY - * @param prevGeoZ - * @param curX - * @param curY - * @param nswe - * @return the LOS Z value - */ - private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) - { - if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) - { - throw new RuntimeException("Multiple directions!"); - } - - if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) - { - return getNearestZ(curX, curY, prevGeoZ); - } - - return getNextHigherZ(curX, curY, prevGeoZ); - } - - /** - * Can see target. Does not check doors between. - * @param xValue the x coordinate - * @param yValue the y coordinate - * @param zValue the z coordinate - * @param txValue the target's x coordinate - * @param tyValue the target's y coordinate - * @param tzValue the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) - { - int x = xValue; - int y = yValue; - int tx = txValue; - int ty = tyValue; - - int geoX = getGeoX(x); - int geoY = getGeoY(y); - int tGeoX = getGeoX(tx); - int tGeoY = getGeoY(ty); - - int z = getNearestZ(geoX, geoY, zValue); - int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - // Fastpath. - if ((geoX == tGeoX) && (geoY == tGeoY)) - { - if (hasGeoPos(tGeoX, tGeoY)) - { - return z == tz; - } - return true; - } - - if (tz > z) - { - int tmp = tx; - tx = x; - x = tmp; - - tmp = ty; - ty = y; - y = tmp; - - tmp = tz; - tz = z; - z = tmp; - - tmp = tGeoX; - tGeoX = geoX; - geoX = tmp; - - tmp = tGeoY; - tGeoY = geoY; - geoY = tmp; - } - - final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); - // First point is guaranteed to be available, skip it, we can always see our own position. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - final int prevZ = pointIter.z(); - int prevGeoZ = prevZ; - int ptIndex = 0; - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - - if ((curX == prevX) && (curY == prevY)) + // Find proper size of buffer. + if (holder._size < size) { continue; } - final int beeCurZ = pointIter.z(); - int curGeoZ = prevGeoZ; - - // Check if the position has geodata. - if (hasGeoPos(curX, curY)) + // Find unlocked NodeBuffer. + for (NodeBuffer buffer : holder._buffer) { - final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); - curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); - int maxHeight; - if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) + if (!buffer.isLocked()) { - maxHeight = z + MAX_SEE_OVER_HEIGHT; - } - else - { - maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; + continue; } - boolean canSeeThrough = false; - if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) + return buffer; + } + + LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); + + // NodeBuffer not found, allocate temporary buffer. + current = new NodeBuffer(holder._size); + current.isLocked(); + } + + return current; + } + + /** + * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + * @return boolean : True, when geodata file was loaded without problem. + */ + private boolean loadGeoBlocks(int regionX, int regionY) + { + final String filename = String.format(Config.GEODATA_TYPE.getFilename(), regionX, regionY); + final String filepath = Config.GEODATA_PATH + "\\" + filename; + + // Standard load. + try (RandomAccessFile raf = new RandomAccessFile(filepath, "r"); + FileChannel fc = raf.getChannel()) + { + // Initialize file buffer. + MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // Load 18B header for L2OFF geodata (1st and 2nd byte...region X and Y). + if (Config.GEODATA_TYPE == GeoType.L2OFF) + { + for (int i = 0; i < 18; i++) { - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) + buffer.get(); + } + } + + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Loop over region blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + if (Config.GEODATA_TYPE == GeoType.L2J) { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) - { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); + // Get block type. + final byte type = buffer.get(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + case GeoStructure.TYPE_MULTILAYER_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + default: + { + throw new IllegalArgumentException("Unknown block type: " + type); + } + } } else { - canSeeThrough = true; + // Get block type. + final short type = buffer.getShort(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + default: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + } } } + } + + // Check data consistency. + if (buffer.remaining() > 0) + { + LOGGER.warning("Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); + } + + // Loading was successful. + return true; + } + catch (Exception e) + { + // An error occured while loading, load null blocks. + LOGGER.warning("Error loading " + filename + " region file. " + e); + + // Replace whole region file with null blocks. + loadNullBlocks(regionX, regionY); + + // Loading was not successful. + return false; + } + } + + /** + * Loads null blocks. Used when no region file is detected or an error occurs during loading. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + */ + private void loadNullBlocks(int regionX, int regionY) + { + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Load all null blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + _blocks[blockX + ix][blockY + iy] = _nullBlock; + } + } + } + + /** + * Converts world X to geodata X. + * @param worldX + * @return int : Geo X + */ + public static int getGeoX(int worldX) + { + return (worldX - World.WORLD_X_MIN) >> 4; + } + + /** + * Converts world Y to geodata Y. + * @param worldY + * @return int : Geo Y + */ + public static int getGeoY(int worldY) + { + return (worldY - World.WORLD_Y_MIN) >> 4; + } + + /** + * Converts geodata X to world X. + * @param geoX + * @return int : World X + */ + public static int getWorldX(int geoX) + { + return (geoX << 4) + World.WORLD_X_MIN + 8; + } + + /** + * Converts geodata Y to world Y. + * @param geoY + * @return int : World Y + */ + public static int getWorldY(int geoY) + { + return (geoY << 4) + World.WORLD_Y_MIN + 8; + } + + /** + * Returns block of geodata on given coordinates. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return {@link ABlock} : Block of geodata. + */ + public ABlock getBlock(int geoX, int geoY) + { + final int x = geoX / GeoStructure.BLOCK_CELLS_X; + if ((x < 0) || (x >= GeoStructure.GEO_BLOCKS_X)) + { + return null; + } + final int y = geoY / GeoStructure.BLOCK_CELLS_Y; + if ((y < 0) || (y >= GeoStructure.GEO_BLOCKS_Y)) + { + return null; + } + return _blocks[x][y]; + } + + /** + * Check if geo coordinates has geo. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return boolean : True, if given geo coordinates have geodata + */ + public boolean hasGeoPos(int geoX, int geoY) + { + final ABlock block = getBlock(geoX, geoY); + return (block != null) && block.hasGeoPos(); + } + + /** + * Returns the height of cell, which is closest to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell geodata Z coordinate, closest to given coordinates. + */ + public short getHeightNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return (short) worldZ; + } + return block.getHeightNearest(geoX, geoY, worldZ); + } + + /** + * Returns the NSWE flag byte of cell, which is closes to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. + */ + public byte getNsweNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return GeoStructure.CELL_FLAG_ALL; + } + return block.getNsweNearest(geoX, geoY, worldZ); + } + + /** + * Check if world coordinates has geo. + * @param worldX : World X + * @param worldY : World Y + * @return boolean : True, if given world coordinates have geodata + */ + public boolean hasGeo(int worldX, int worldY) + { + return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param loc : The location used as reference. + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(Location loc) + { + return getHeightNearest(getGeoX(loc.getX()), getGeoY(loc.getY()), loc.getZ()); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param worldX : world x + * @param worldY : world y + * @param worldZ : world z + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(int worldX, int worldY, int worldZ) + { + return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); + } + + /** + * Check line of sight from {@link WorldObject} to {@link WorldObject}.
+ * @param object : The origin object. + * @param target : The target object. + * @return True, when object can see target. + */ + public boolean canSeeTarget(WorldObject object, WorldObject target) + { + // Can always see doors. + if (target.isDoor()) + { + return true; + } + + if (object.getInstanceWorld() != target.getInstanceWorld()) + { + return false; + } + + if (DoorData.getInstance().checkIfDoorsBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld())) + { + return false; + } + + // Get object's and target's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + double theight = 0; + if (target instanceof Creature) + { + theight += (((Creature) target).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + return canSee(object.getX(), object.getY(), object.getZ(), oheight, target.getX(), target.getY(), target.getZ(), theight) && canSee(target.getX(), target.getY(), target.getZ(), theight, object.getX(), object.getY(), object.getZ(), oheight); + } + + /** + * Check line of sight from {@link WorldObject} to {@link Location}.
+ * Note: The check uses {@link Location}'s real Z coordinate (e.g. point above ground), not its geodata representation. + * @param object : The origin object. + * @param position : The target position. + * @return True, when object can see position. + */ + public boolean canSeeLocation(WorldObject object, Location position) + { + // Get object and location coordinates. + int ox = object.getX(); + int oy = object.getY(); + int oz = object.getZ(); + int tx = position.getX(); + int ty = position.getY(); + int tz = position.getZ(); + + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld())) + { + return false; + } + + // Get object's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + // Perform geodata check. + return canSee(ox, oy, oz, oheight, tx, ty, tz, 0) && canSee(tx, ty, tz, 0, ox, oy, oz, oheight); + } + + /** + * Simple check for origin to target visibility.
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param oheight : The height of origin, used as start point. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param theight : The height of target, used as end point. + * @return True, when origin can see target. + */ + public boolean canSee(int ox, int oy, int oz, double oheight, int tx, int ty, int tz, double theight) + { + // Get origin geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get target geodata coordinates. + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check being on same cell and layer (index). + // Note: Get index must use origin height increased by cell height, the method returns index to height exclusive self. + int index = block.getIndexBelow(gox, goy, oz + GeoStructure.CELL_HEIGHT); + if (index < 0) + { + return false; + } + + if ((gox == gtx) && (goy == gty)) + { + return index == block.getIndexBelow(gtx, gty, tz + GeoStructure.CELL_HEIGHT); + } + + // Get ground and nswe flag. + int groundZ = block.getHeight(index); + int nswe = block.getNswe(index); + + // Get delta coordinates, slope of line (XY, XZ) and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double dz = (tz + theight) - (oz + oheight); + final double m = (double) dy / dx; + final double mz = dz / Math.sqrt((dx * dx) + (dy * dy)); + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); + + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) + { + // Set next cell in X direction. + gridX += mdt.getStepX(); + gox += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); - if (!canSeeThrough) - { - return false; - } + // Set next cell in Y direction. + gridY += mdt.getStepY(); + goy += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevGeoZ = curGeoZ; - ++ptIndex; + // Get block of the next cell. + block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get line of sight height (including Z slope). + double losz = oz + oheight + Config.MAX_OBSTACLE_HEIGHT; + losz += mz * Math.sqrt(((checkX - ox) * (checkX - ox)) + ((checkY - oy) * (checkY - oy))); + + // Check line of sight going though wall (vertical check). + + // Get index of particular layer, based on last iterated cell conditions. + boolean canMove = (nswe & dir) != 0; + if (canMove) + { + // No wall present, get next cell below current cell. + index = block.getIndexBelow(gox, goy, groundZ + GeoStructure.CELL_IGNORE_HEIGHT); + } + else + { + // Wall present, get next cell above current cell. + index = block.getIndexAbove(gox, goy, groundZ - (2 * GeoStructure.CELL_HEIGHT)); + } + + // Next cell's does not exist (no geodata with valid condition), return fail. + if (index < 0) + { + return false; + } + + // Get next cell's layer height. + int z = block.getHeight(index); + + // Perform sine of sight check (next cell is above line of sight line), return fail. + if (z > losz) + { + return false; + } + + // Next cell is accessible, update z and NSWE. + groundZ = z; + nswe = block.getNswe(index); } + // Iteration is completed, no obstacle is found. return true; } /** - * Move check. - * @param x the x coordinate - * @param y the y coordinate - * @param zValue the z coordinate - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tzValue the target's z coordinate - * @param instance the instance - * @return the last Location (x,y,z) where player can walk - just before wall + * Check movement from coordinates to coordinates.
+ * Note: The Z coordinates are supposed to be already validated geodata coordinates. + * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return True, when target coordinates are reachable from origin coordinates. */ - public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(x); - final int geoY = getGeoY(y); - final int z = getNearestZ(geoX, geoY, zValue); - final int tGeoX = getGeoX(tx); - final int tGeoY = getGeoY(ty); - final int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return new Location(x, y, getHeight(x, y, z)); - } - if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) - { - return new Location(x, y, getHeight(x, y, z)); + return false; } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = z; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return false; + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + int goz = getHeightNearest(gox, goy, oz); + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check movement within same cell. + if ((gox == gtx) && (goy == gty)) + { + return goz == getHeight(tx, ty, tz); + } + + // Get nswe flag. + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - // Can't move, return previous location. - return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check point heading into obstacle, if so return current point. + if ((nswe & dir) == 0) + { + return false; + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return false; + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != tz)) - { - // Different floors, return start location. - return new Location(x, y, z); - } - - return new Location(tx, ty, tz); + // When origin Z is target Z, the move is successful. + return goz == getHeight(tx, ty, tz); } /** - * Checks if its possible to move from one location to another. - * @param fromX the X coordinate to start checking from - * @param fromY the Y coordinate to start checking from - * @param fromZvalue the Z coordinate to start checking from - * @param toX the X coordinate to end checking at - * @param toY the Y coordinate to end checking at - * @param toZvalue the Z coordinate to end checking at - * @param instance the instance - * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + * Check movement from origin to target coordinates. Returns last available point in the checked path.
+ * Target X and Y reachable and Z is on same floor: + *
    + *
  • Location of the target with corrected Z value from geodata.
  • + *
+ * Target X and Y reachable but Z is on another floor: + *
    + *
  • Location of the origin with corrected Z value from geodata.
  • + *
+ * Target X and Y not reachable: + *
    + *
  • Last accessible location in destination to target.
  • + *
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return The {@link Location} representing last point of movement (e.g. just before wall). */ - public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + public Location getValidLocation(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(fromX); - final int geoY = getGeoY(fromY); - final int fromZ = getNearestZ(geoX, geoY, fromZvalue); - final int tGeoX = getGeoX(toX); - final int tGeoY = getGeoY(toY); - final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); - - if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) - { - return false; + return new Location(ox, oy, oz); } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = fromZ; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return new Location(ox, oy, oz); + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + final int gtz = getHeightNearest(gtx, gty, tz); + int goz = getHeightNearest(gox, goy, oz); + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - return false; - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check target cell is out of geodata grid (world coordinates). + if ((nx < 0) || (nx >= GeoStructure.GEO_CELLS_X) || (ny < 0) || (ny >= GeoStructure.GEO_CELLS_Y)) + { + return new Location(checkX, checkY, goz); + } + + // Check point heading into obstacle, if so return current (border) point. + if ((nswe & dir) == 0) + { + return new Location(checkX, checkY, goz); + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current (border) point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return new Location(checkX, checkY, goz); + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) - { - // Different floors. - return false; - } - - return true; + // Compare Z coordinates: + // If same, path is okay, return target point and fix its Z geodata coordinate. + // If not same, path is does not exist, return origin point. + return goz == gtz ? new Location(tx, ty, gtz) : new Location(ox, oy, oz); } + /** + * Returns the list of location objects as a result of complete path calculation. + * @param ox : origin x + * @param oy : origin y + * @param oz : origin z + * @param tx : target x + * @param ty : target y + * @param tz : target z + * @param instance + * @return {@code List} : complete path from nodes + */ + public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + { + // Get origin and check existing geo coords. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + if (!hasGeoPos(gox, goy)) + { + return Collections.emptyList(); + } + + int goz = getHeightNearest(gox, goy, oz); + + // Get target and check existing geo coords. + int gtx = getGeoX(tx); + int gty = getGeoY(ty); + if (!hasGeoPos(gtx, gty)) + { + return Collections.emptyList(); + } + + int gtz = getHeightNearest(gtx, gty, tz); + + // Prepare buffer for pathfinding calculations. + NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + if (buffer == null) + { + return Collections.emptyList(); + } + + // Find path. + List path = null; + try + { + path = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + if (path.isEmpty()) + { + return Collections.emptyList(); + } + } + catch (Exception e) + { + return Collections.emptyList(); + } + finally + { + buffer.free(); + } + + // Check path. + if (path.size() < 3) + { + return path; + } + + // Get path list iterator. + ListIterator point = path.listIterator(); + + // Get node A (origin). + int nodeAx = ox; + int nodeAy = oy; + int nodeAz = goz; + + // Get node B. + Location nodeB = point.next(); + + // Iterate thought the path to optimize it. + while (point.hasNext()) + { + // Get node C. + Location nodeC = path.get(point.nextIndex()); + + // Check movement from node A to node C. + if (canMoveToTarget(nodeAx, nodeAy, nodeAz, nodeC.getX(), nodeC.getY(), nodeC.getZ(), instance)) + { + // Can move from node A to node C. + // Remove node B. + point.remove(); + } + else + { + // Can not move from node A to node C. + // Set node A (node B is part of path, update A coordinates). + nodeAx = nodeB.getX(); + nodeAy = nodeB.getY(); + nodeAz = nodeB.getZ(); + } + + // Set node B. + nodeB = point.next(); + } + + return path; + } + + /** + * NodeBuffer container with specified size and count of separate buffers. + */ + private static class BufferHolder + { + final int _size; + final List _buffer; + + public BufferHolder(int size, int count) + { + _size = size; + _buffer = new ArrayList<>(count); + + for (int i = 0; i < count; i++) + { + _buffer.add(new NodeBuffer(size)); + } + } + } + + /** + * Returns the instance of the {@link GeoEngine}. + * @return {@link GeoEngine} : The instance. + */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -737,4 +1054,4 @@ public class GeoEngine { protected static final GeoEngine INSTANCE = new GeoEngine(); } -} +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java deleted file mode 100644 index cc76b7523a..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ /dev/null @@ -1,301 +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 . - */ -package org.l2jmobius.gameserver.geoengine; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; -import org.l2jmobius.gameserver.model.World; -import org.l2jmobius.gameserver.model.instancezone.Instance; - -/** - * @author -Nemesiss- - */ -public class GeoEnginePathfinding -{ - private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); - - private BufferInfo[] _buffers; - - protected GeoEnginePathfinding() - { - try - { - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - - _buffers = 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); - } - - _buffers[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"); - } - } - - public boolean pathNodesExist(short regionoffset) - { - return false; - } - - public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) - { - 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)))); - if (buffer == null) - { - return null; - } - - List path = null; - try - { - final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); - - if (result == null) - { - return null; - } - - path = constructPath(result); - } - catch (Exception e) - { - LOGGER.warning(e.getMessage()); - return null; - } - finally - { - buffer.free(); - } - - // check path - if (path.size() < 3) - { - return path; - } - - int currentX, currentY, currentZ; - ListIterator middlePoint; - - middlePoint = path.listIterator(); - currentX = x; - currentY = y; - currentZ = z; - - while (middlePoint.hasNext()) - { - final AbstractNodeLoc locMiddle = middlePoint.next(); - if (!middlePoint.hasNext()) - { - break; - } - - final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); - if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) - { - middlePoint.remove(); - } - else - { - currentX = locMiddle.getX(); - currentY = locMiddle.getY(); - currentZ = locMiddle.getZ(); - } - } - - return path; - } - - /** - * Convert geodata position to pathnode position - * @param geo_pos - * @return pathnode position - */ - public short getNodePos(int geo_pos) - { - return (short) (geo_pos >> 3); // OK? - } - - /** - * Convert node position to pathnode block position - * @param node_pos - * @return pathnode block position (0...255) - */ - public short getNodeBlock(int node_pos) - { - return (short) (node_pos % 256); - } - - public byte getRegionX(int node_pos) - { - return (byte) ((node_pos >> 8) + World.TILE_X_MIN); - } - - public byte getRegionY(int node_pos) - { - return (byte) ((node_pos >> 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 node_x rx - * @return - */ - public int calculateWorldX(short node_x) - { - return World.MAP_MIN_X + (node_x * 128) + 48; - } - - /** - * Convert pathnode y to World y position - * @param node_y - * @return - */ - public int calculateWorldY(short node_y) - { - return World.MAP_MIN_Y + (node_y * 128) + 48; - } - - private List constructPath(AbstractNode nodeValue) - { - final LinkedList path = new LinkedList<>(); - int previousDirectionX = Integer.MIN_VALUE; - int previousDirectionY = Integer.MIN_VALUE; - int directionX, directionY; - - AbstractNode node = nodeValue; - while (node.getParent() != null) - { - directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); - directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); - - // only add a new route point if moving direction changes - if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) - { - previousDirectionX = directionX; - previousDirectionY = directionY; - - path.addFirst(node.getLoc()); - node.setLoc(null); - } - - node = node.getParent(); - } - - return path; - } - - private CellNodeBuffer alloc(int size) - { - CellNodeBuffer current = null; - for (BufferInfo i : _buffers) - { - if (i.mapSize >= size) - { - for (CellNodeBuffer buf : i.buffer) - { - if (buf.lock()) - { - current = buf; - break; - } - } - if (current != null) - { - break; - } - - // not found, allocate temporary buffer - current = new CellNodeBuffer(i.mapSize); - current.lock(); - if (i.buffer.size() < i.count) - { - i.buffer.add(current); - break; - } - } - } - - return current; - } - - private static final class BufferInfo - { - final int mapSize; - final int count; - ArrayList buffer; - - public BufferInfo(int size, int cnt) - { - mapSize = size; - count = cnt; - buffer = new ArrayList<>(count); - } - } - - public static GeoEnginePathfinding getInstance() - { - return SingletonHolder.INSTANCE; - } - - private static class SingletonHolder - { - protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); - } -} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java new file mode 100644 index 0000000000..49ec35aa1c --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java @@ -0,0 +1,85 @@ +/* + * 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 . + */ +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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @param index : Index of the cell. + * @return short : Cell geodata Z coordinate, below given coordinates. + */ + public abstract byte getNswe(int index); +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java new file mode 100644 index 0000000000..f5997bf287 --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java @@ -0,0 +1,130 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java new file mode 100644 index 0000000000..9af9d2a28a --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java @@ -0,0 +1,95 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java new file mode 100644 index 0000000000..31fbf5d477 --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java new file mode 100644 index 0000000000..f77d21d84b --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java @@ -0,0 +1,68 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +public class BlockNull extends ABlock +{ + @Override + public boolean hasGeoPos() + { + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java deleted file mode 100644 index 61ea4fcf82..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java +++ /dev/null @@ -1,48 +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 . - */ -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() - { - } -} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java deleted file mode 100644 index 3c8de1a7f7..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java +++ /dev/null @@ -1,77 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java deleted file mode 100644 index 1ccdefe3da..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java +++ /dev/null @@ -1,56 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java new file mode 100644 index 0000000000..691b164e0c --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ +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_ALL = 0x0F; + + // 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; +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java deleted file mode 100644 index 25eafc5a04..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java +++ /dev/null @@ -1,42 +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 . - */ -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); -} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java deleted file mode 100644 index 0d2c765002..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java +++ /dev/null @@ -1,47 +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 . - */ -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(); -} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java deleted file mode 100644 index 52df457360..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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); - - // 1 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; - } -} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java deleted file mode 100644 index 72a5a635c7..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java +++ /dev/null @@ -1,55 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author HorridoJoho - */ -public final class NullRegion implements IRegion -{ - public static final NullRegion INSTANCE = new NullRegion(); - - @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 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; - } -} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java deleted file mode 100644 index fc924cb875..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java +++ /dev/null @@ -1,92 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java deleted file mode 100644 index 5087513ed9..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java +++ /dev/null @@ -1,87 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public abstract class AbstractNode -{ - private T _loc; - private AbstractNode _parent; - - public AbstractNode(T loc) - { - _loc = loc; - } - - public void setParent(AbstractNode p) - { - _parent = p; - } - - public AbstractNode getParent() - { - return _parent; - } - - public T getLoc() - { - return _loc; - } - - public void setLoc(T l) - { - _loc = l; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); - return result; - } - - @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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java deleted file mode 100644 index 7223b5b2be..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java +++ /dev/null @@ -1,33 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -/** - * @author -Nemesiss- - */ -public abstract class AbstractNodeLoc -{ - public abstract int getX(); - - public abstract int getY(); - - public abstract int getZ(); - - public abstract int getNodeX(); - - public abstract int getNodeY(); -} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java deleted file mode 100644 index 3425234e0e..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public class CellNode extends AbstractNode -{ - 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java deleted file mode 100644 index 522610bb7c..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java +++ /dev/null @@ -1,348 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -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 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) - { - _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(); - } - - public final List debugPath() - { - final List 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 void getNeighbors() - { - if (!_current.getLoc().canGoAll()) - { - 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); - } - - // SouthEast - if ((nodeE != null) && (nodeS != null)) - { - if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) - { - addNode(x + 1, y + 1, z, true); - } - } - - // SouthWest - if ((nodeS != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) - { - addNode(x - 1, y + 1, z, true); - } - } - - // NorthEast - if ((nodeN != null) && (nodeE != null)) - { - if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) - { - addNode(x + 1, y - 1, z, true); - } - } - - // NorthWest - if ((nodeN != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) - { - addNode(x - 1, y - 1, z, true); - } - } - } - - private 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(); - // reinit node if needed - if (result.getLoc() != null) - { - result.getLoc().set(x, y, z); - } - else - { - result.setLoc(new NodeLoc(x, y, z)); - } - } - - return result; - } - - private 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 boolean isHighWeight(int x, int y, int z) - { - final CellNode result = getNode(x, y, z); - if (result == null) - { - return true; - } - - if (!result.getLoc().canGoAll()) - { - return true; - } - if (Math.abs(result.getLoc().getZ() - z) > 16) - { - return true; - } - - return false; - } - - private 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java new file mode 100644 index 0000000000..fa5ca43c2f --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -0,0 +1,111 @@ +/* + * 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 . + */ +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 geodata values. + private int _geoX; + private int _geoY; + private byte _nswe; + + // 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; + + _costG = 0; + _costH = 0; + _costF = 0; + + _parent = null; + } + + public void setGeo(int gx, int gy, int gz, byte nswe) + { + super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); + + _geoX = gx; + _geoY = gy; + _nswe = nswe; + } + + 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 int getCostF() + { + return _costF; + } + + public Node getParent() + { + return _parent; + } + + @Override + public int compareTo(Node o) + { + return _costF - o._costF; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java new file mode 100644 index 0000000000..b464212c96 --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -0,0 +1,368 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.awt.Color; +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; +import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; + +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 _opened; + // Container holding Nodes already explored. + private final List _closed; + + // Target coordinates. + private int _gtx; + private int _gty; + private int _gtz; + + // Pathfinding statistics. + private long _timeStamp; + private long _lastElapsedTime; + + 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 findPath(int gox, int goy, int goz, int gtx, int gty, int gtz) + { + // Set start timestamp. + _timeStamp = System.currentTimeMillis(); + + // 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)); + _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 constructPath() + { + // Create result. + final LinkedList 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; + } + + /** + * Creates list of Nodes to show debug path. + * @param debug : The debug packet to add debug informations in. + */ + public void debugPath(ExServerPrimitive debug) + { + // Add all opened node as yellow points. + for (Node n : _opened) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); + } + + // Add all opened node as blue points. + for (Node n : _closed) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); + } + } + + 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; + + _lastElapsedTime = System.currentTimeMillis() - _timeStamp; + _lock.unlock(); + } + + public long getElapsedTime() + { + return _lastElapsedTime; + } + + /** + * Expand the current {@link Node} by exploring its neighbors (axially and diagonally). + */ + private void expand() + { + // Movement is blocked, skip. + final byte nswe = _current.getNSWE(); + if (nswe == 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; + + // Can move north, expand. + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + } + + // Can move south, expand. + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + } + + // Can move west, expand. + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move east, expand. + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move north-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move north-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + } + + /** + * 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 weight : The weight of movement to the new node. + * @return The nswe of the added node. Blank, if not added. + */ + private byte addNode(int gx, int gy, int gzValue, int weight) + { + // 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]; + + // Set node geodata coordinates. + node.setGeo(gx, gy, gz, nswe); + + // 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, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + _opened.add(node); + _bufferIndex++; + return nswe; + } + + /** + * Calculate cost H value, calculated using diagonal distance method.
+ * 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. + final int dx = Math.abs(gx - _gtx); + final int dy = Math.abs(gy - _gty); + final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + + // Get diagonal and axial differences to the target. + final int dd = Math.min(dx, dy); + final int da = Math.max(dx, dy) - dd; + + // Calculate the diagonal distance of the node to the target. + return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java deleted file mode 100644 index e3bad04b25..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; - -/** - * @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 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 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; - // return super.hashCode(); - } - - @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; - if (_x != other._x) - { - return false; - } - if (_y != other._y) - { - return false; - } - if (_goNorth != other._goNorth) - { - return false; - } - if (_goEast != other._goEast) - { - return false; - } - if (_goSouth != other._goSouth) - { - return false; - } - if (_goWest != other._goWest) - { - return false; - } - if (_geoHeight != other._geoHeight) - { - return false; - } - return true; - } -} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java index 1faf1ae423..ed261765e1 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java @@ -93,15 +93,15 @@ public class ZoneManager implements IXmlReader private static final Map SETTINGS = new HashMap<>(); private static final int SHIFT_BY = 15; - private static final int OFFSET_X = Math.abs(World.MAP_MIN_X >> SHIFT_BY); - private static final int OFFSET_Y = Math.abs(World.MAP_MIN_Y >> SHIFT_BY); + private static final int OFFSET_X = Math.abs(World.WORLD_X_MIN >> SHIFT_BY); + private static final int OFFSET_Y = Math.abs(World.WORLD_Y_MIN >> SHIFT_BY); private final Map, ConcurrentHashMap> _classZones = new ConcurrentHashMap<>(); private final Map _spawnTerritories = new ConcurrentHashMap<>(); private final AtomicInteger _lastDynamicId = new AtomicInteger(300000); private List _debugItems; - private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.MAP_MAX_X >> SHIFT_BY) + OFFSET_X + 1][(World.MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y + 1]; + private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.WORLD_X_MAX >> SHIFT_BY) + OFFSET_X + 1][(World.WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y + 1]; /** * Instantiates a new zone manager. diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/Location.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/Location.java index 73689ab1a6..ca3b22a3f0 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/Location.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/Location.java @@ -16,30 +16,30 @@ */ package org.l2jmobius.gameserver.model; +import java.util.Objects; + +import org.l2jmobius.commons.util.Point2D; import org.l2jmobius.gameserver.model.interfaces.ILocational; import org.l2jmobius.gameserver.model.interfaces.IPositionable; /** - * Location data transfer object.
- * Contains coordinates data, heading and instance Id. - * @author Zoey76 + * A datatype used to retain a 3D (x/y/z/heading) point. It got the capability to be set and cleaned. */ -public class Location implements IPositionable +public class Location extends Point2D implements IPositionable { - protected int _x; - protected int _y; - protected int _z; - private int _heading; + protected volatile int _z; + protected volatile int _heading; public Location(int x, int y, int z) { - this(x, y, z, 0); + super(x, y); + _z = z; + _heading = 0; } public Location(int x, int y, int z, int heading) { - _x = x; - _y = y; + super(x, y); _z = z; _heading = heading; } @@ -51,9 +51,8 @@ public class Location implements IPositionable public Location(StatSet set) { - _x = set.getInt("x"); - _y = set.getInt("y"); - _z = set.getInt("z"); + super(set.getInt("x", 0), set.getInt("y", 0)); + _z = set.getInt("z", 0); _heading = set.getInt("heading", 0); } @@ -146,6 +145,25 @@ public class Location implements IPositionable _heading = loc.getHeading(); } + @Override + public void clean() + { + super.clean(); + _z = 0; + } + + @Override + public Location clone() + { + return new Location(_x, _y, _z); + } + + @Override + public int hashCode() + { + return (31 * super.hashCode()) + Objects.hash(_z); + } + @Override public boolean equals(Object obj) { diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/World.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/World.java index c1510e8c0e..0d796dd834 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/World.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/World.java @@ -66,19 +66,19 @@ public class World public static final int TILE_Y_MAX = 26; public static final int TILE_ZERO_COORD_X = 20; public static final int TILE_ZERO_COORD_Y = 18; - public static final int MAP_MIN_X = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; - public static final int MAP_MIN_Y = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; + public static final int WORLD_X_MIN = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; + public static final int WORLD_Y_MIN = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; - public static final int MAP_MAX_X = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; - public static final int MAP_MAX_Y = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; + public static final int WORLD_X_MAX = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; + public static final int WORLD_Y_MAX = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; /** Calculated offset used so top left region is 0,0 */ - public static final int OFFSET_X = Math.abs(MAP_MIN_X >> SHIFT_BY); - public static final int OFFSET_Y = Math.abs(MAP_MIN_Y >> SHIFT_BY); + public static final int OFFSET_X = Math.abs(WORLD_X_MIN >> SHIFT_BY); + public static final int OFFSET_Y = Math.abs(WORLD_Y_MIN >> SHIFT_BY); /** Number of regions. */ - private static final int REGIONS_X = (MAP_MAX_X >> SHIFT_BY) + OFFSET_X; - private static final int REGIONS_Y = (MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y; + private static final int REGIONS_X = (WORLD_X_MAX >> SHIFT_BY) + OFFSET_X; + private static final int REGIONS_Y = (WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y; /** Map containing all the players in game. */ private static final Map _allPlayers = new ConcurrentHashMap<>(); @@ -817,9 +817,6 @@ public class World return _memberInPartyNumber.get(); } - /** - * @return the current instance of World - */ public static World getInstance() { return SingletonHolder.INSTANCE; diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/WorldObject.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/WorldObject.java index 0e5536bbc0..62313ffea1 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/WorldObject.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/WorldObject.java @@ -188,23 +188,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif synchronized (this) { int spawnX = x; - if (spawnX > World.MAP_MAX_X) + if (spawnX > World.WORLD_X_MAX) { - spawnX = World.MAP_MAX_X - 5000; + spawnX = World.WORLD_X_MAX - 5000; } - if (spawnX < World.MAP_MIN_X) + if (spawnX < World.WORLD_X_MIN) { - spawnX = World.MAP_MIN_X + 5000; + spawnX = World.WORLD_X_MIN + 5000; } int spawnY = y; - if (spawnY > World.MAP_MAX_Y) + if (spawnY > World.WORLD_Y_MAX) { - spawnY = World.MAP_MAX_Y - 5000; + spawnY = World.WORLD_Y_MAX - 5000; } - if (spawnY < World.MAP_MIN_Y) + if (spawnY < World.WORLD_Y_MIN) { - spawnY = World.MAP_MIN_Y + 5000; + spawnY = World.WORLD_Y_MIN + 5000; } // Set the x,y,z position of the WorldObject. If flagged with _isSpawned, setXYZ will automatically update world region, so avoid that. @@ -518,23 +518,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif public void setXYZInvisible(int x, int y, int z) { int correctX = x; - if (correctX > World.MAP_MAX_X) + if (correctX > World.WORLD_X_MAX) { - correctX = World.MAP_MAX_X - 5000; + correctX = World.WORLD_X_MAX - 5000; } - if (correctX < World.MAP_MIN_X) + if (correctX < World.WORLD_X_MIN) { - correctX = World.MAP_MIN_X + 5000; + correctX = World.WORLD_X_MIN + 5000; } int correctY = y; - if (correctY > World.MAP_MAX_Y) + if (correctY > World.WORLD_Y_MAX) { - correctY = World.MAP_MAX_Y - 5000; + correctY = World.WORLD_Y_MAX - 5000; } - if (correctY < World.MAP_MIN_Y) + if (correctY < World.WORLD_Y_MIN) { - correctY = World.MAP_MIN_Y + 5000; + correctY = World.WORLD_Y_MIN + 5000; } setXYZ(correctX, correctY, z); diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/Creature.java index fc29cca657..8593d97e36 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -65,8 +65,6 @@ 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.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -2585,7 +2583,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3406,8 +3404,8 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe final int originalX = x; final int originalY = y; final int originalZ = z; - final int gtx = (originalX - World.MAP_MIN_X) >> 4; - final int gty = (originalY - World.MAP_MIN_Y) >> 4; + final int gtx = (originalX - World.WORLD_X_MIN) >> 4; + final int gty = (originalY - World.WORLD_Y_MIN) >> 4; if (isOnGeodataPath()) { try @@ -3430,7 +3428,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe && !(((curZ - z) > 300) && (distance < 300))) // Prohibit correcting destination if character wants to fall. { // location different if destination wasn't reached (or just z coord is different) - final Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(curX, curY, curZ, x, y, z, getInstanceWorld()); + final Location destiny = GeoEngine.getInstance().getValidLocation(curX, curY, curZ, x, y, z, getInstanceWorld()); x = destiny.getX(); y = destiny.getY(); z = destiny.getZ(); @@ -3444,7 +3442,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { if (isPlayer() && !_isFlying && !isInWater) diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/Summon.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/Summon.java index 2aaa8064dd..4b09eca1d7 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/Summon.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/actor/Summon.java @@ -106,7 +106,7 @@ public abstract class Summon extends Playable final int x = owner.getX(); final int y = owner.getY(); final int z = owner.getZ(); - final Location location = GeoEngine.getInstance().canMoveToTargetLoc(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, owner.getInstanceWorld()); + final Location location = GeoEngine.getInstance().getValidLocation(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, getInstanceWorld()); setXYZInvisible(location.getX(), location.getY(), location.getZ()); } diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java index 334bab47ea..9b0c7059f1 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java @@ -1507,7 +1507,7 @@ public class ItemInstance extends WorldObject if (dropper != null) { final Instance instance = dropper.getInstanceWorld(); - final Location dropDest = GeoEngine.getInstance().canMoveToTargetLoc(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); + final Location dropDest = GeoEngine.getInstance().getValidLocation(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); x = dropDest.getX(); y = dropDest.getY(); z = dropDest.getZ(); diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java index 5d294fe47c..558bf92edf 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java @@ -1179,7 +1179,7 @@ public class SkillCaster implements Runnable } } - final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().canMoveToTargetLoc(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); + final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().getValidLocation(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); creature.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); creature.broadcastPacket(new FlyToLocation(creature, destination, flyType, 0, 0, 333)); diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java index c62c821f44..30068134e3 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java @@ -114,7 +114,7 @@ public class ValidatePosition implements IClientIncomingPacket { if (player.isFalling(_z)) { - final int nearestZ = GeoEngine.getInstance().getHigherHeight(_x, _y, _z); + final int nearestZ = GeoEngine.getInstance().getHeight(_x, _y, _z); if (player.getZ() < nearestZ) { player.setXYZ(_x, _y, nearestZ); diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/util/GeoUtils.java index 4600a0fc53..19a979819e 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -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.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; @@ -30,21 +30,21 @@ public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getInstance().getWorldX(iter.x()); - final int wy = GeoEngine.getInstance().getWorldY(iter.y()); + final int wx = GeoEngine.getWorldX(iter.x()); + final int wy = GeoEngine.getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public final class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); - int wy = GeoEngine.getInstance().getWorldY(prevY); + int wx = GeoEngine.getWorldX(prevX); + int wy = GeoEngine.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.getInstance().getWorldX(curX); - wy = GeoEngine.getInstance().getWorldY(curY); + wx = GeoEngine.getWorldX(curX); + wy = GeoEngine.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().checkNearestNswe(x, y, z, nswe)) + if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0) { return Color.GREEN; } @@ -109,9 +109,8 @@ public final class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final GeoEngine ge = GeoEngine.getInstance(); - final int playerGx = ge.getGeoX(player.getX()); - final int playerGy = ge.getGeoY(player.getY()); + final int playerGx = GeoEngine.getGeoX(player.getX()); + final int playerGy = GeoEngine.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -135,32 +134,32 @@ public final class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = ge.getWorldX(gx); - final int y = ge.getWorldY(gy); - final int z = ge.getNearestZ(gx, gy, player.getZ()); + final int x = GeoEngine.getWorldX(gx); + final int y = GeoEngine.getWorldY(gy); + final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); + Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); 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, Cell.NSWE_EAST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); 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, Cell.NSWE_SOUTH); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); 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, Cell.NSWE_WEST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); 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); @@ -188,41 +187,41 @@ public final class GeoUtils { if (y > lastY) { - return Cell.NSWE_SOUTH_EAST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E; } else if (y < lastY) { - return Cell.NSWE_NORTH_EAST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E; } else { - return Cell.NSWE_EAST; + return GeoStructure.CELL_FLAG_E; } } else if (x < lastX) // west { if (y > lastY) { - return Cell.NSWE_SOUTH_WEST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W; } else if (y < lastY) { - return Cell.NSWE_NORTH_WEST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W; } else { - return Cell.NSWE_WEST; + return GeoStructure.CELL_FLAG_W; } } else // unchanged x { if (y > lastY) { - return Cell.NSWE_SOUTH; + return GeoStructure.CELL_FLAG_S; } else if (y < lastY) { - return Cell.NSWE_NORTH; + return GeoStructure.CELL_FLAG_N; } else { diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/config/GeoEngine.ini b/L2J_Mobius_Classic_2.1_Zaken/dist/game/config/GeoEngine.ini index 609e8a89a7..e740c678dd 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Classic_2.1_Zaken/dist/game/config/GeoEngine.ini @@ -6,33 +6,44 @@ # at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ 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 + # ================================================================= -# Path finding +# Pathfinding # ================================================================= # 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 +# an alternative path (e.g. walk around obstacle), default: True +PathFinding = True -# Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 -PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 +# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 -# Weight for nodes without obstacles far from walls -LowWeight = 0.5 +# Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 +MoveWeight = 10 +MoveWeightDiag = 14 -# Weight for nodes near walls -MediumWeight = 2 +# 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 -# Weight for nodes with obstacles -HighWeight = 3 +# 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. -# Default: LowWeight * sqrt(2) -DiagonalWeight = 0.707 +# Maximum number of generated nodes per one path-finding process, default 3500 +MaxIterations = 3500 # ================================================================= -# Other +# Line of Sight # ================================================================= -# Correct player Z after falling through the ground. -CorrectPlayerZ = False +# 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 diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/geodata/L2D_GeoEngine.patch b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/geodata/L2D_GeoEngine.patch deleted file mode 100644 index f8577e3a0e..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/geodata/L2D_GeoEngine.patch +++ /dev/null @@ -1,6035 +0,0 @@ -Index: dist/game/GeoDataConverter.bat -=================================================================== ---- dist/game/GeoDataConverter.bat (nonexistent) -+++ dist/game/GeoDataConverter.bat (working copy) -@@ -0,0 +1,6 @@ -+@echo off -+title L2D geodata converter -+ -+java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter -+ -+pause -Index: dist/game/GeoDataConverter.sh -=================================================================== ---- dist/game/GeoDataConverter.sh (nonexistent) -+++ dist/game/GeoDataConverter.sh (working copy) -@@ -0,0 +1,4 @@ -+#! /bin/sh -+ -+java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 -+ -Index: dist/game/config/GeoEngine.ini -=================================================================== ---- dist/game/config/GeoEngine.ini (revision 8409) -+++ dist/game/config/GeoEngine.ini (working copy) -@@ -1,6 +1,10 @@ - # ================================================================= - # Geodata - # ================================================================= -+# Because of real-time performance we are using geodata files only in -+# diagonal L2D format now (using filename e.g. 22_16.l2d). -+# L2D geodata can be obtained by conversion of existing L2J or L2OFF geodata. -+# Launch "GeoDataConverter.bat/sh" and follow instructions to start the conversion. - - # 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/ -@@ -13,6 +17,16 @@ - CoordSynchronize = 2 - - # ================================================================= -+# Path checking -+# ================================================================= -+ -+# 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 finding - # ================================================================= - -@@ -23,19 +37,23 @@ - # Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - --# Weight for nodes without obstacles far from walls --LowWeight = 0.5 -+# Base path weight, when moving from one node to another on axis direction, default: 10 -+BaseWeight = 10 - --# Weight for nodes near walls --MediumWeight = 2 -+# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -+DiagonalWeight = 14 - --# Weight for nodes with obstacles --HighWeight = 3 -+# When movement flags of target node is blocked to any direction, multiply movement weight by this multiplier. -+# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 10 -+ObstacleMultiplier = 10 - --# Weight for diagonal movement. --# Default: LowWeight * sqrt(2) --DiagonalWeight = 0.707 -+# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 20 -+# For proper function must be higher than BaseWeight and/or DiagonalWeight. -+HeuristicWeight = 20 - -+# Maximum number of generated nodes per one path-finding process, default 3500 -+MaxIterations = 3500 -+ - # ================================================================= - # Other - # ================================================================= -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (working copy) -@@ -55,9 +55,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -@@ -73,9 +72,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (working copy) -@@ -18,11 +18,11 @@ - - import java.util.List; - --import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -+import org.l2jmobius.gameserver.geoengine.GeoEngine; - import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -+import org.l2jmobius.gameserver.network.SystemMessageId; - import org.l2jmobius.gameserver.util.BuilderUtil; - - public class AdminPathNode implements IAdminCommandHandler -@@ -37,29 +37,30 @@ - { - if (command.equals("admin_path_find")) - { -- if (!Config.PATHFINDING) -- { -- BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); -- return true; -- } - if (activeChar.getTarget() != null) - { -- final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); -+ final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); - if (path == null) - { -- BuilderUtil.sendSysMessage(activeChar, "No Route!"); -- return true; -+ BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); - } -- for (AbstractNodeLoc a : path) -+ else - { -- BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); -+ for (Location point : path) -+ { -+ BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); -+ } - } - } - else - { -- BuilderUtil.sendSysMessage(activeChar, "No Target!"); -+ activeChar.sendPacket(SystemMessageId.INVALID_TARGET); - } - } -+ else -+ { -+ return false; -+ } - return true; - } - -Index: java/org/l2jmobius/Config.java -=================================================================== ---- java/org/l2jmobius/Config.java (revision 8409) -+++ java/org/l2jmobius/Config.java (working copy) -@@ -32,7 +32,6 @@ - import java.net.UnknownHostException; - import java.nio.charset.StandardCharsets; - import java.nio.file.Files; --import java.nio.file.Path; - import java.nio.file.Paths; - import java.time.Duration; - import java.util.ArrayList; -@@ -966,14 +965,17 @@ - // -------------------------------------------------- - // GeoEngine - // -------------------------------------------------- -- public static Path GEODATA_PATH; -+ public static String GEODATA_PATH; -+ public static int COORD_SYNCHRONIZE; -+ public static int PART_OF_CHARACTER_HEIGHT; -+ public static int MAX_OBSTACLE_HEIGHT; - public static boolean PATHFINDING; - public static String PATHFIND_BUFFERS; -- public static float LOW_WEIGHT; -- public static float MEDIUM_WEIGHT; -- public static float HIGH_WEIGHT; -- public static float DIAGONAL_WEIGHT; -- public static int COORD_SYNCHRONIZE; -+ public static int BASE_WEIGHT; -+ public static int DIAGONAL_WEIGHT; -+ public static int HEURISTIC_WEIGHT; -+ public static int OBSTACLE_MULTIPLIER; -+ public static int MAX_ITERATIONS; - public static boolean CORRECT_PLAYER_Z; - - /** Attribute System */ -@@ -2568,14 +2570,17 @@ - - // Load GeoEngine config file (if exists) - final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); -- GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); -+ GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); -+ COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); -+ MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); - PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -- LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); -- MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); -- HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); -- DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); -- COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); -+ DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); -+ OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); -+ HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); -+ MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); - CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); - - // Load AllowedPlayerRaces config file (if exists) -Index: java/org/l2jmobius/gameserver/geoengine/GeoEngine.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (revision 8435) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (working copy) -@@ -16,100 +16,90 @@ - */ - package org.l2jmobius.gameserver.geoengine; - --import java.io.IOException; -+import java.io.File; - import java.io.RandomAccessFile; - import java.nio.ByteOrder; --import java.nio.channels.FileChannel.MapMode; --import java.nio.file.Files; --import java.nio.file.Path; --import java.util.concurrent.atomic.AtomicReferenceArray; --import java.util.logging.Level; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.List; - import java.util.logging.Logger; - - import org.l2jmobius.Config; - import org.l2jmobius.gameserver.data.xml.DoorData; - import org.l2jmobius.gameserver.data.xml.FenceData; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; --import org.l2jmobius.gameserver.geoengine.geodata.IRegion; --import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; --import org.l2jmobius.gameserver.geoengine.geodata.Region; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.World; - import org.l2jmobius.gameserver.model.WorldObject; -+import org.l2jmobius.gameserver.model.actor.Creature; - import org.l2jmobius.gameserver.model.instancezone.Instance; --import org.l2jmobius.gameserver.model.interfaces.ILocational; --import org.l2jmobius.gameserver.util.GeoUtils; --import org.l2jmobius.gameserver.util.LinePointIterator; --import org.l2jmobius.gameserver.util.LinePointIterator3D; -+import org.l2jmobius.gameserver.util.MathUtil; - - /** -- * @author -Nemesiss-, HorridoJoho -+ * @author Hasha - */ - public class GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); -+ protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - -- private static final int WORLD_MIN_X = -655360; -- private static final int WORLD_MIN_Y = -589824; -- private static final int WORLD_MIN_Z = -16384; -+ private final ABlock[][] _blocks; -+ private final BlockNull _nullBlock; - -- /** 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; -- -- /** The regions array */ -- private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); -- -- private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; -- private static final int ELEVATED_SEE_OVER_DISTANCE = 2; -- private static final int MAX_SEE_OVER_HEIGHT = 48; -- -- protected GeoEngine() -+ /** -+ * GeoEngine constructor. Loads all geodata files of chosen geodata format. -+ */ -+ public GeoEngine() - { - LOGGER.info("GeoEngine: Initializing..."); -- for (int i = 0; i < _regions.length(); i++) -- { -- _regions.set(i, NullRegion.INSTANCE); -- } - -+ // initialize block container -+ _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; -+ -+ // load null block -+ _nullBlock = new BlockNull(); -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files according to geoengine config setup - int loaded = 0; -- try -+ long fileSize = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { -- for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { -- for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) -+ final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); -+ if (f.exists() && !f.isDirectory()) - { -- final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); -- if (Files.exists(geoFilePath)) -+ // region file is load-able, try to load it -+ if (loadGeoBlocks(rx, ry)) - { -- try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) -- { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -- loaded++; -- } -+ loaded++; -+ fileSize += f.length(); - } - } -+ else -+ { -+ // region file is not load-able, load null blocks -+ loadNullBlocks(rx, ry); -+ } - } - } -- catch (Exception e) -+ -+ LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -+ if (loaded > 0) - { -- LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); -- System.exit(1); -+ LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); - } - -- LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -- -- // Avoid wrong configs when no files are loaded. -+ // avoid wrong configs when no files are loaded - if (loaded == 0) - { - if (Config.PATHFINDING) -@@ -123,619 +113,820 @@ - LOGGER.info("GeoEngine: Forcing CoordSynchronize setting to -1."); - } - } -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); - } - - /** -- * @param geoX -- * @param geoY -- * @return the region -+ * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. -+ * @return boolean : True, when geodata file was loaded without problem. - */ -- private IRegion getRegion(int geoX, int geoY) -+ private final boolean loadGeoBlocks(int regionX, int regionY) - { -- final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); -- if ((region < 0) || (region >= _regions.length())) -+ final String filename = String.format(GeoFormat.L2D.getFilename(), regionX, regionY); -+ -+ // standard load -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) - { -- return null; -+ // initialize file buffer -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, GeoFormat.L2D); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ } -+ -+ // check data consistency -+ if (buffer.remaining() > 0) -+ { -+ LOGGER.warning("GeoEngine: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ } -+ -+ // loading was successful -+ return true; - } -- return _regions.get(region); -- } -- -- /** -- * @param filePath -- * @param regionX -- * @param regionY -- * @throws IOException -- */ -- 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")) -+ catch (Exception e) - { -- _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -+ // an error occured while loading, load null blocks -+ LOGGER.warning("GeoEngine: Error while loading " + filename + " region file."); -+ LOGGER.warning(e.getMessage()); -+ -+ // replace whole region file with null blocks -+ loadNullBlocks(regionX, regionY); -+ -+ // loading was not successful -+ return false; - } - } - - /** -- * @param regionX -- * @param regionY -+ * Loads null blocks. Used when no region file is detected or an error occurs during loading. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. - */ -- public void unloadRegion(int regionX, int regionY) -+ private final void loadNullBlocks(int regionX, int regionY) - { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); -- } -- -- /** -- * @param geoX -- * @param geoY -- * @return if geodata exist -- */ -- public boolean hasGeoPos(int geoX, int geoY) -- { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // load all null blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { -- return false; -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[blockX + ix][blockY + iy] = _nullBlock; -+ } - } -- return region.hasGeo(); - } - -+ // GEODATA - GENERAL -+ - /** -- * Checks the specified position for available geodata. -- * @param x the world x -- * @param y the world y -- * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise -+ * Converts world X to geodata X. -+ * @param worldX -+ * @return int : Geo X - */ -- public boolean hasGeo(int x, int y) -+ public static int getGeoX(int worldX) - { -- return hasGeoPos(getGeoX(x), getGeoY(y)); -+ return (MathUtil.limit(worldX, World.MAP_MIN_X, World.MAP_MAX_X) - World.MAP_MIN_X) >> 4; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe check -+ * Converts world Y to geodata Y. -+ * @param worldY -+ * @return int : Geo Y - */ -- public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) -+ public static int getGeoY(int worldY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return true; -- } -- return region.checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(worldY, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.MAP_MIN_Y) >> 4; - } - - /** -+ * Converts geodata X to world X. - * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe anti-corner cut -+ * @return int : World X - */ -- public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) -+ public static int getWorldX(int geoX) - { -- boolean can = true; -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- return can && checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(geoX, 0, GeoStructure.GEO_CELLS_X) << 4) + World.MAP_MIN_X + 8; - } - - /** -- * @param geoX -+ * Converts geodata Y to world Y. - * @param geoY -- * @param worldZ -- * @return the nearest Z value -+ * @return int : World Y - */ -- public int getNearestZ(int geoX, int geoY, int worldZ) -+ public static int getWorldY(int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return worldZ; -- } -- return region.getNearestZ(geoX, geoY, worldZ); -+ return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next lower Z value -+ * Returns block of geodata on given coordinates. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return {@link ABlock} : Block of geodata. - */ -- public int getNextLowerZ(int geoX, int geoY, int worldZ) -+ private final ABlock getBlock(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final int x = geoX / GeoStructure.BLOCK_CELLS_X; -+ final int y = geoY / GeoStructure.BLOCK_CELLS_Y; -+ -+ // if x or y is out of array return null -+ if ((x > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) - { -- return worldZ; -+ return _blocks[x][y]; - } -- return region.getNextLowerZ(geoX, geoY, worldZ); -+ return null; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next higher Z value -+ * Check if geo coordinates has geo. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return boolean : True, if given geo coordinates have geodata - */ -- public int getNextHigherZ(int geoX, int geoY, int worldZ) -+ public boolean hasGeoPos(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final ABlock block = getBlock(geoX, geoY); -+ if (block == null) // null block check - { -- return worldZ; -+ // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) -+ // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); -+ return false; - } -- return region.getNextHigherZ(geoX, geoY, worldZ); -+ return block.hasGeoPos(); - } - - /** -- * Gets the Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the height of cell, which is closest to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, closest to given coordinates. - */ -- public int getHeight(int x, int y, int z) -+ public short getHeightNearest(int geoX, int geoY, int worldZ) - { -- return getNearestZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getHeightNearest(geoX, geoY, worldZ) : (short) worldZ; - } - - /** -- * Gets the next lower Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the NSWE flag byte of cell, which is closes to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. - */ -- public int getLowerHeight(int x, int y, int z) -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) - { -- return getNextLowerZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getNsweNearest(geoX, geoY, worldZ) : (byte) 0xFF; - } - - /** -- * Gets the next higher Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Check if world coordinates has geo. -+ * @param worldX : World X -+ * @param worldY : World Y -+ * @return boolean : True, if given world coordinates have geodata - */ -- public int getHigherHeight(int x, int y, int z) -+ public boolean hasGeo(int worldX, int worldY) - { -- return getNextHigherZ(getGeoX(x), getGeoY(y), z); -+ return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); - } - - /** -- * @param worldX -- * @return the geo X -+ * Returns closest Z coordinate according to geodata. -+ * @param worldX : world x -+ * @param worldY : world y -+ * @param worldZ : world z -+ * @return short : nearest Z coordinates according to geodata - */ -- public int getGeoX(int worldX) -+ public short getHeight(int worldX, int worldY, int worldZ) - { -- return (worldX - WORLD_MIN_X) / 16; -+ return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - -- /** -- * @param worldY -- * @return the geo Y -- */ -- public int getGeoY(int worldY) -- { -- return (worldY - WORLD_MIN_Y) / 16; -- } -+ // PATHFINDING - - /** -- * @param worldZ -- * @return the geo Z -+ * Check line of sight from {@link WorldObject} to {@link WorldObject}. -+ * @param origin : The origin object. -+ * @param target : The target object. -+ * @return {@code boolean} : True if origin can see target - */ -- public int getGeoZ(int worldZ) -+ public boolean canSeeTarget(WorldObject origin, WorldObject target) - { -- return (worldZ - WORLD_MIN_Z) / 16; -- } -- -- /** -- * @param geoX -- * @return the world X -- */ -- public int getWorldX(int geoX) -- { -- return (geoX * 16) + WORLD_MIN_X + 8; -- } -- -- /** -- * @param geoY -- * @return the world Y -- */ -- public int getWorldY(int geoY) -- { -- return (geoY * 16) + WORLD_MIN_Y + 8; -- } -- -- /** -- * @param geoZ -- * @return the world Z -- */ -- public int getWorldZ(int geoZ) -- { -- return (geoZ * 16) + WORLD_MIN_Z + 8; -- } -- -- /** -- * Can see target. Doors as target always return true. Checks doors between. -- * @param cha the character -- * @param target the target -- * @return {@code true} if the character can see the target (LOS), {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, WorldObject target) -- { -- if (target.isDoor()) -+ if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) - { -- // Can always see doors. - return true; - } -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param cha the character -- * @param worldPosition the world position -- * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) -- { -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param world -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @param tworld the target's instanceId -- * @return -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) -- { -- return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instance -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) -- { -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) -+ -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = target.getX(); -+ final int ty = target.getY(); -+ final int tz = target.getZ(); -+ -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } -- return canSeeTarget(x, y, z, tx, ty, tz); -- } -- -- /** -- * @param prevX -- * @param prevY -- * @param prevGeoZ -- * @param curX -- * @param curY -- * @param nswe -- * @return the LOS Z value -- */ -- private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) -- { -- if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { -- throw new RuntimeException("Multiple directions!"); -+ return false; - } - -- if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- return getNearestZ(curX, curY, prevGeoZ); -+ return true; - } - -- return getNextHigherZ(curX, curY, prevGeoZ); -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) -+ { -+ return true; -+ } -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) -+ { -+ return goz == gtz; -+ } -+ -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) -+ { -+ oheight = ((Creature) origin).getCollisionHeight() * 2; -+ } -+ -+ double theight = 0; -+ if (target.isCreature()) -+ { -+ theight = ((Creature) target).getCollisionHeight() * 2; -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); - } - - /** -- * Can see target. Does not check doors between. -- * @param xValue the x coordinate -- * @param yValue the y coordinate -- * @param zValue the z coordinate -- * @param txValue the target's x coordinate -- * @param tyValue the target's y coordinate -- * @param tzValue the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -+ * Check line of sight from {@link WorldObject} to {@link Location}. -+ * @param origin : The origin object. -+ * @param position : The target position. -+ * @return {@code boolean} : True if object can see position - */ -- public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) -+ public boolean canSeeTarget(WorldObject origin, Location position) - { -- int x = xValue; -- int y = yValue; -- int tx = txValue; -- int ty = tyValue; -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = position.getX(); -+ final int ty = position.getY(); -+ final int tz = position.getZ(); - -- int geoX = getGeoX(x); -- int geoY = getGeoY(y); -- int tGeoX = getGeoX(tx); -- int tGeoY = getGeoY(ty); -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) -+ { -+ return false; -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) -+ { -+ return false; -+ } - -- int z = getNearestZ(geoX, geoY, zValue); -- int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- // Fastpath. -- if ((geoX == tGeoX) && (geoY == tGeoY)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- if (hasGeoPos(tGeoX, tGeoY)) -- { -- return z == tz; -- } - return true; - } - -- if (tz > z) -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) - { -- int tmp = tx; -- tx = x; -- x = tmp; -- -- tmp = ty; -- ty = y; -- y = tmp; -- -- tmp = tz; -- tz = z; -- z = tmp; -- -- tmp = tGeoX; -- tGeoX = geoX; -- geoX = tmp; -- -- tmp = tGeoY; -- tGeoY = geoY; -- geoY = tmp; -+ return goz == gtz; - } - -- final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); -- // First point is guaranteed to be available, skip it, we can always see our own position. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- final int prevZ = pointIter.z(); -- int prevGeoZ = prevZ; -- int ptIndex = 0; -- while (pointIter.next()) -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight(); -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); -+ } -+ -+ /** -+ * Simple check for origin to target visibility. -+ * @param goxValue : origin X geodata coordinate -+ * @param goyValue : origin Y geodata coordinate -+ * @param gozValue : origin Z geodata coordinate -+ * @param oheight : origin height (if instance of {@link Character}) -+ * @param gtxValue : target X geodata coordinate -+ * @param gtyValue : target Y geodata coordinate -+ * @param gtzValue : target Z geodata coordinate -+ * @param theight : target height (if instance of {@link Character}) -+ * @param instance -+ * @return {@code boolean} : True, when target can be seen. -+ */ -+ private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) -+ { -+ int goz = gozValue; -+ int gtz = gtzValue; -+ int gox = goxValue; -+ int goy = goyValue; -+ int gtx = gtxValue; -+ int gty = gtyValue; -+ -+ // get line of sight Z coordinates -+ double losoz = goz + ((oheight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ double lostz = gtz + ((theight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ -+ // get X delta and signum -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirox = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; -+ final byte dirtx = sx > 0 ? GeoStructure.CELL_FLAG_W : GeoStructure.CELL_FLAG_E; -+ -+ // get Y delta and signum -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte diroy = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ final byte dirty = sy > 0 ? GeoStructure.CELL_FLAG_N : GeoStructure.CELL_FLAG_S; -+ -+ // get Z delta -+ final int dm = Math.max(dx, dy); -+ final double dz = (lostz - losoz) / dm; -+ -+ // get direction flag for diagonal movement -+ final byte diroxy = getDirXY(dirox, diroy); -+ final byte dirtxy = getDirXY(dirtx, dirty); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte diro; -+ byte dirt; -+ -+ // initialize node values -+ int nox = gox; -+ int noy = goy; -+ int ntx = gtx; -+ int nty = gty; -+ byte nsweo = getNsweNearest(gox, goy, goz); -+ byte nswet = getNsweNearest(gtx, gty, gtz); -+ -+ // loop -+ ABlock block; -+ int index; -+ for (int i = 0; i < ((dm + 1) / 2); i++) -+ { -+ // reset direction flag -+ diro = 0; -+ dirt = 0; - -- if ((curX == prevX) && (curY == prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- continue; -+ // calculate next point XY coordinates -+ d -= dy; -+ d += dx; -+ nox += sx; -+ ntx -= sx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroxy; -+ dirt |= dirtxy; - } -+ else if (e2 > -dy) -+ { -+ // calculate next point X coordinate -+ d -= dy; -+ nox += sx; -+ ntx -= sx; -+ diro |= dirox; -+ dirt |= dirtx; -+ } -+ else if (e2 < dx) -+ { -+ // calculate next point Y coordinate -+ d += dx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroy; -+ dirt |= dirty; -+ } - -- final int beeCurZ = pointIter.z(); -- int curGeoZ = prevGeoZ; -- -- // Check if the position has geodata. -- if (hasGeoPos(curX, curY)) - { -- final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); -- curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); -- int maxHeight; -- if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) -+ // get block of the next cell -+ block = getBlock(nox, noy); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nsweo & diro) == 0) - { -- maxHeight = z + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); - } - else - { -- maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); - } - -- boolean canSeeThrough = false; -- if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) -+ // layer does not exist, return -+ if (index == -1) - { -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else -- { -- canSeeThrough = true; -- } -+ return false; - } - -- if (!canSeeThrough) -+ // get layer and next line of sight Z coordinate -+ goz = block.getHeight(index); -+ losoz += dz; -+ -+ // perform line of sight check, return when fails -+ if ((goz - losoz) > Config.MAX_OBSTACLE_HEIGHT) - { - return false; - } -+ -+ // get layer nswe -+ nsweo = block.getNswe(index); - } -+ { -+ // get block of the next cell -+ block = getBlock(ntx, nty); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nswet & dirt) == 0) -+ { -+ index = block.getIndexAbove(ntx, nty, gtz - GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ else -+ { -+ index = block.getIndexBelow(ntx, nty, gtz + GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ -+ // layer does not exist, return -+ if (index == -1) -+ { -+ return false; -+ } -+ -+ // get layer and next line of sight Z coordinate -+ gtz = block.getHeight(index); -+ lostz -= dz; -+ -+ // perform line of sight check, return when fails -+ if ((gtz - lostz) > Config.MAX_OBSTACLE_HEIGHT) -+ { -+ return false; -+ } -+ -+ // get layer nswe -+ nswet = block.getNswe(index); -+ } - -- prevX = curX; -- prevY = curY; -- prevGeoZ = curGeoZ; -- ++ptIndex; -+ // update coords -+ gox = nox; -+ goy = noy; -+ gtx = ntx; -+ gty = nty; - } - -- return true; -+ // when iteration is completed, compare final Z coordinates -+ return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); - } - - /** -- * Move check. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param zValue the z coordinate -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tzValue the target's z coordinate -- * @param instance the instance -- * @return the last Location (x,y,z) where player can walk - just before wall -+ * Check movement from coordinates to coordinates. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {code boolean} : True if target coordinates are reachable from origin coordinates - */ -- public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) -+ public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- final int geoX = getGeoX(x); -- final int geoY = getGeoY(y); -- final int z = getNearestZ(geoX, geoY, zValue); -- final int tGeoX = getGeoX(tx); -- final int tGeoY = getGeoY(ty); -- final int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } -- if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = z; -+ // perform geodata check -+ final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); -+ return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); -+ } -+ -+ /** -+ * Check movement from origin to target. Returns last available point in the checked path. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {@link Location} : Last point where object can walk (just before wall) -+ */ -+ public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) -+ { -+ return new Location(ox, oy, oz); -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) -+ { -+ return new Location(ox, oy, oz); -+ } - -- while (pointIter.next()) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -- -- if (hasGeoPos(prevX, prevY)) -- { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -- { -- // Can't move, return previous location. -- return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); -- } -- } -- -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return new Location(tx, ty, tz); - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != tz)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- // Different floors, return start location. -- return new Location(x, y, z); -+ return new Location(tx, ty, tz); - } - -- return new Location(tx, ty, tz); -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) -+ { -+ return new Location(tx, ty, tz); -+ } -+ -+ // perform geodata check -+ return checkMove(gox, goy, goz, gtx, gty, gtz, instance); - } - - /** -- * Checks if its possible to move from one location to another. -- * @param fromX the X coordinate to start checking from -- * @param fromY the Y coordinate to start checking from -- * @param fromZvalue the Z coordinate to start checking from -- * @param toX the X coordinate to end checking at -- * @param toY the Y coordinate to end checking at -- * @param toZvalue the Z coordinate to end checking at -- * @param instance the instance -- * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise -+ * With this method you can check if a position is visible or can be reached by beeline movement.
-+ * Target X and Y reachable and Z is on same floor: -+ *
    -+ *
  • Location of the target with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y reachable but Z is on another floor: -+ *
    -+ *
  • Location of the origin with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y not reachable: -+ *
    -+ *
  • Last accessible location in destination to target.
  • -+ *
-+ * @param gox : origin X geodata coordinate -+ * @param goy : origin Y geodata coordinate -+ * @param goz : origin Z geodata coordinate -+ * @param gtx : target X geodata coordinate -+ * @param gty : target Y geodata coordinate -+ * @param gtz : target Z geodata coordinate -+ * @param instance -+ * @return {@link GeoLocation} : The last allowed point of movement. - */ -- public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) -+ protected final GeoLocation checkMove(int gox, int goy, int goz, int gtx, int gty, int gtz, Instance instance) - { -- final int geoX = getGeoX(fromX); -- final int geoY = getGeoY(fromY); -- final int fromZ = getNearestZ(geoX, geoY, fromZvalue); -- final int tGeoX = getGeoX(toX); -- final int tGeoY = getGeoY(toY); -- final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); -- -- if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) -+ if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } -- if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) -+ if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = fromZ; -+ // get X delta, signum and direction flag -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirX = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; - -- while (pointIter.next()) -+ // get Y delta, signum and direction flag -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte dirY = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ -+ // get direction flag for diagonal movement -+ final byte dirXY = getDirXY(dirX, dirY); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte direction; -+ -+ // load pointer coordinates -+ int gpx = gox; -+ int gpy = goy; -+ int gpz = goz; -+ -+ // load next pointer -+ int nx = gpx; -+ int ny = gpy; -+ -+ // loop -+ int count = 0; -+ while (count++ < Config.MAX_ITERATIONS) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -+ direction = 0; - -- if (hasGeoPos(prevX, prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -+ d -= dy; -+ d += dx; -+ nx += sx; -+ ny += sy; -+ direction |= dirXY; -+ } -+ else if (e2 > -dy) -+ { -+ d -= dy; -+ nx += sx; -+ direction |= dirX; -+ } -+ else if (e2 < dx) -+ { -+ d += dx; -+ ny += sy; -+ direction |= dirY; -+ } -+ -+ // obstacle found, return -+ if ((getNsweNearest(gpx, gpy, gpz) & direction) == 0) -+ { -+ return new GeoLocation(gpx, gpy, gpz); -+ } -+ -+ // update pointer coordinates -+ gpx = nx; -+ gpy = ny; -+ gpz = getHeightNearest(nx, ny, gpz); -+ -+ // target coordinates reached -+ if ((gpx == gtx) && (gpy == gty)) -+ { -+ if (gpz == gtz) - { -- return false; -+ // path found, Z coordinates are okay, return target point -+ return new GeoLocation(gtx, gty, gtz); - } -+ -+ // path found, Z coordinates are not okay, return last good point -+ return new GeoLocation(gpx, gpy, gpz); - } -+ } -+ -+ return new GeoLocation(gox, goy, goz); -+ } -+ -+ /** -+ * Returns diagonal NSWE flag format of combined two NSWE flags. -+ * @param dirX : X direction NSWE flag -+ * @param dirY : Y direction NSWE flag -+ * @return byte : NSWE flag of combined direction -+ */ -+ private static byte getDirXY(byte dirX, byte dirY) -+ { -+ // check axis directions -+ if (dirY == GeoStructure.CELL_FLAG_N) -+ { -+ if (dirX == GeoStructure.CELL_FLAG_W) -+ { -+ return GeoStructure.CELL_FLAG_NW; -+ } - -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return GeoStructure.CELL_FLAG_NE; - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) -+ if (dirX == GeoStructure.CELL_FLAG_W) - { -- // Different floors. -- return false; -+ return GeoStructure.CELL_FLAG_SW; - } - -- return true; -+ return GeoStructure.CELL_FLAG_SE; - } - -+ /** -+ * Returns the list of location objects as a result of complete path calculation. -+ * @param ox : origin x -+ * @param oy : origin y -+ * @param oz : origin z -+ * @param tx : target x -+ * @param ty : target y -+ * @param tz : target z -+ * @param instance -+ * @return {@code List} : complete path from nodes -+ */ -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ return null; -+ } -+ -+ /** -+ * Returns the instance of the {@link GeoEngine}. -+ * @return {@link GeoEngine} : The instance. -+ */ - public static GeoEngine getInstance() - { - return SingletonHolder.INSTANCE; -@@ -743,6 +934,6 @@ - - private static class SingletonHolder - { -- protected static final GeoEngine INSTANCE = new GeoEngine(); -+ protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); - } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (working copy) -@@ -20,88 +20,84 @@ - import java.util.LinkedList; - import java.util.List; - import java.util.ListIterator; --import java.util.logging.Level; --import java.util.logging.Logger; - - import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; --import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; --import org.l2jmobius.gameserver.model.World; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -+import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.instancezone.Instance; - - /** -- * @author -Nemesiss- -+ * @author Hasha - */ --public class GeoEnginePathfinding -+final class GeoEnginePathfinding extends GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); -+ // pre-allocated buffers -+ private final BufferHolder[] _buffers; - -- private BufferInfo[] _buffers; -- - protected GeoEnginePathfinding() - { -- try -+ super(); -+ -+ final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ _buffers = new BufferHolder[array.length]; -+ int count = 0; -+ for (int i = 0; i < array.length; i++) - { -- final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ final String buf = array[i]; -+ final String[] args = buf.split("x"); - -- _buffers = new BufferInfo[array.length]; -- -- String buf; -- String[] args; -- for (int i = 0; i < array.length; i++) -+ try - { -- buf = array[i]; -- args = buf.split("x"); -- if (args.length != 2) -- { -- throw new Exception("Invalid buffer definition: " + buf); -- } -- -- _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); -+ final int size = Integer.parseInt(args[1]); -+ count += size; -+ _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); - } -+ catch (Exception e) -+ { -+ LOGGER.warning("GeoEnginePathfinding: Can not load buffer setting: " + buf); -+ } - } -- catch (Exception e) -- { -- LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); -- throw new Error("CellPathFinding: load aborted"); -- } -+ -+ LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); - } - -- public boolean pathNodesExist(short regionoffset) -+ @Override -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- return false; -- } -- -- public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) -- { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -- if (!GeoEngine.getInstance().hasGeo(x, y)) -+ // get origin and check existing geo coords -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { - 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)) -+ -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coords -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { - 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)))); -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // Prepare buffer for pathfinding calculations -+ final NodeBuffer buffer = getBuffer(64 + (2 * Math.max(Math.abs(gox - gtx), Math.abs(goy - gty)))); - if (buffer == null) - { - return null; - } - -- List path = null; -+ // find path -+ List path = null; - try - { -- final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); -- -+ final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); - if (result == null) - { - return null; -@@ -125,33 +121,45 @@ - return path; - } - -- int currentX, currentY, currentZ; -- ListIterator middlePoint; -+ // get path list iterator -+ final ListIterator point = path.listIterator(); - -- middlePoint = path.listIterator(); -- currentX = x; -- currentY = y; -- currentZ = z; -+ // get node A (origin) -+ int nodeAx = gox; -+ int nodeAy = goy; -+ short nodeAz = goz; - -- while (middlePoint.hasNext()) -+ // get node B -+ GeoLocation nodeB = (GeoLocation) point.next(); -+ -+ // iterate thought the path to optimize it -+ int count = 0; -+ while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) - { -- final AbstractNodeLoc locMiddle = middlePoint.next(); -- if (!middlePoint.hasNext()) -- { -- break; -- } -+ // get node C -+ final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - -- final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); -- if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) -+ // check movement from node A to node C -+ final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); -+ if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) - { -- middlePoint.remove(); -+ // can move from node A to node C -+ -+ // remove node B -+ point.remove(); - } - else - { -- currentX = locMiddle.getX(); -- currentY = locMiddle.getY(); -- currentZ = locMiddle.getZ(); -+ // can not move from node A to node C -+ -+ // set node A (node B is part of path, update A coordinates) -+ nodeAx = nodeB.getGeoX(); -+ nodeAy = nodeB.getGeoY(); -+ nodeAz = (short) nodeB.getZ(); - } -+ -+ // set node B -+ nodeB = (GeoLocation) point.next(); - } - - return path; -@@ -158,144 +166,102 @@ - } - - /** -- * Convert geodata position to pathnode position -- * @param geo_pos -- * @return pathnode position -+ * Create list of node locations as result of calculated buffer node tree. -+ * @param node : the entry point -+ * @return List : list of node location - */ -- public short getNodePos(int geo_pos) -+ private static List constructPath(Node node) - { -- return (short) (geo_pos >> 3); // OK? -- } -- -- /** -- * Convert node position to pathnode block position -- * @param node_pos -- * @return pathnode block position (0...255) -- */ -- public short getNodeBlock(int node_pos) -- { -- return (short) (node_pos % 256); -- } -- -- public byte getRegionX(int node_pos) -- { -- return (byte) ((node_pos >> 8) + World.TILE_X_MIN); -- } -- -- public byte getRegionY(int node_pos) -- { -- return (byte) ((node_pos >> 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 node_x rx -- * @return -- */ -- public int calculateWorldX(short node_x) -- { -- return World.MAP_MIN_X + (node_x * 128) + 48; -- } -- -- /** -- * Convert pathnode y to World y position -- * @param node_y -- * @return -- */ -- public int calculateWorldY(short node_y) -- { -- return World.MAP_MIN_Y + (node_y * 128) + 48; -- } -- -- private List constructPath(AbstractNode nodeValue) -- { -- final LinkedList path = new LinkedList<>(); -- int previousDirectionX = Integer.MIN_VALUE; -- int previousDirectionY = Integer.MIN_VALUE; -- int directionX, directionY; -+ // create empty list -+ final LinkedList list = new LinkedList<>(); - -- AbstractNode node = nodeValue; -- while (node.getParent() != null) -+ // set direction X/Y -+ int dx = 0; -+ int dy = 0; -+ -+ // get target parent -+ Node target = node; -+ Node parent = target.getParent(); -+ -+ // while parent exists -+ int count = 0; -+ while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { -- directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); -- directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); -+ // get parent <> target direction X/Y -+ final int nx = parent.getLoc().getGeoX() - target.getLoc().getGeoX(); -+ final int ny = parent.getLoc().getGeoY() - target.getLoc().getGeoY(); - -- // only add a new route point if moving direction changes -- if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) -+ // direction has changed? -+ if ((dx != nx) || (dy != ny)) - { -- previousDirectionX = directionX; -- previousDirectionY = directionY; -+ // add node to the beginning of the list -+ list.addFirst(target.getLoc()); - -- path.addFirst(node.getLoc()); -- node.setLoc(null); -+ // update direction X/Y -+ dx = nx; -+ dy = ny; - } - -- node = node.getParent(); -+ // move to next node, set target and get its parent -+ target = parent; -+ parent = target.getParent(); - } - -- return path; -+ // return list -+ return list; - } - -- private CellNodeBuffer alloc(int size) -+ /** -+ * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer. -+ * @param size : pre-calculated minimal required size -+ * @return NodeBuffer : buffer -+ */ -+ private final NodeBuffer getBuffer(int size) - { -- CellNodeBuffer current = null; -- for (BufferInfo i : _buffers) -+ NodeBuffer current = null; -+ for (BufferHolder holder : _buffers) - { -- if (i.mapSize >= size) -+ // Find proper size of buffer -+ if (holder._size < size) - { -- for (CellNodeBuffer buf : i.buffer) -+ continue; -+ } -+ -+ // Find unlocked NodeBuffer -+ for (NodeBuffer buffer : holder._buffer) -+ { -+ if (!buffer.isLocked()) - { -- if (buf.lock()) -- { -- current = buf; -- break; -- } -+ continue; - } -- if (current != null) -- { -- break; -- } - -- // not found, allocate temporary buffer -- current = new CellNodeBuffer(i.mapSize); -- current.lock(); -- if (i.buffer.size() < i.count) -- { -- i.buffer.add(current); -- break; -- } -+ return buffer; - } -+ -+ // NodeBuffer not found, allocate temporary buffer -+ current = new NodeBuffer(holder._size); -+ current.isLocked(); - } - - return current; - } - -- private static final class BufferInfo -+ /** -+ * NodeBuffer container with specified size and count of separate buffers. -+ */ -+ private static final class BufferHolder - { -- final int mapSize; -- final int count; -- ArrayList buffer; -+ final int _size; -+ List _buffer; - -- public BufferInfo(int size, int cnt) -+ public BufferHolder(int size, int count) - { -- mapSize = size; -- count = cnt; -- buffer = new ArrayList<>(count); -+ _size = size; -+ _buffer = new ArrayList<>(count); -+ for (int i = 0; i < count; i++) -+ { -+ _buffer.add(new NodeBuffer(size)); -+ } - } - } -- -- public static GeoEnginePathfinding getInstance() -- { -- return SingletonHolder.INSTANCE; -- } -- -- private static class SingletonHolder -- { -- protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); -- } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (working copy) -@@ -0,0 +1,197 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @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 height of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getHeightNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first above given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, above given coordinates. -+ */ -+ public abstract short getHeightAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first below given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is closest to given coordinates. -+ * @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 the NSWE flag byte of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getNsweNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first above given coordinates. -+ * @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 getNsweAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first below given coordinates. -+ * @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 getNsweBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is closes layer to given coordinates. -+ * @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. -+ * @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 above given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is first layer below given coordinates. -+ * @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 index to data of the cell, which is first layer below given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexBelowOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeight(int index); -+ -+ /** -+ * Returns the height of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightOriginal(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNswe(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNsweOriginal(int index); -+ -+ /** -+ * Sets the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @param nswe : New NSWE flag byte. -+ */ -+ public abstract void setNswe(int index, byte nswe); -+ -+ /** -+ * Saves the block in L2D format to {@link BufferedOutputStream}. Used only for L2D geodata conversion. -+ * @param stream : The stream. -+ * @throws IOException : Can't save the block to steam. -+ */ -+ public abstract void saveBlock(BufferedOutputStream stream) throws IOException; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (working copy) -@@ -0,0 +1,252 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockComplex(ByteBuffer bb, GeoFormat format) -+ { -+ // initialize buffer -+ _buffer = new byte[GeoStructure.BLOCK_CELLS * 3]; -+ -+ // load data -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // 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); -+ } -+ else -+ { -+ // get nswe -+ final byte nswe = bb.get(); -+ _buffer[i * 3] = nswe; -+ -+ // get height -+ final short height = bb.getShort(); -+ _buffer[(i * 3) + 1] = (byte) (height & 0x00FF); -+ _buffer[(i * 3) + 2] = (byte) (height >> 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height > worldZ ? height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height < worldZ ? height : Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public 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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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 int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_COMPLEX_L2D); -+ -+ // write block data -+ stream.write(_buffer, 0, GeoStructure.BLOCK_CELLS * 3); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (working copy) -@@ -0,0 +1,176 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockFlat extends ABlock -+{ -+ protected final short _height; -+ protected byte _nswe; -+ -+ /** -+ * Creates FlatBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockFlat(ByteBuffer bb, GeoFormat format) -+ { -+ _height = bb.getShort(); -+ _nswe = format != GeoFormat.L2D ? 0x0F : (byte) (0xFF); -+ if (format == GeoFormat.L2OFF) -+ { -+ bb.getShort(); -+ } -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return true; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height > worldZ ? _height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height < worldZ ? _height : Short.MAX_VALUE; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height > worldZ ? _nswe : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height < worldZ ? _nswe : 0; -+ } -+ -+ @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return index -+ return _height < worldZ ? 0 : -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _nswe = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_FLAT_L2D); -+ -+ // write height -+ stream.write((byte) (_height & 0x00FF)); -+ stream.write((byte) (_height >> 8)); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (working copy) -@@ -0,0 +1,465 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+import java.nio.ByteOrder; -+import java.util.Arrays; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockMultilayer extends ABlock -+{ -+ private static final int MAX_LAYERS = Byte.MAX_VALUE; -+ -+ private static ByteBuffer _temp; -+ -+ /** -+ * Initializes the temporarily buffer. -+ */ -+ public static void initialize() -+ { -+ // initialize temporarily buffer and sorting mechanism -+ _temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3); -+ _temp.order(ByteOrder.LITTLE_ENDIAN); -+ } -+ -+ /** -+ * Releases temporarily buffer. -+ */ -+ public static void release() -+ { -+ _temp = null; -+ } -+ -+ protected byte[] _buffer; -+ -+ /** -+ * Implicit constructor for children class. -+ */ -+ protected BlockMultilayer() -+ { -+ _buffer = null; -+ } -+ -+ /** -+ * Creates MultilayerBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockMultilayer(ByteBuffer bb, GeoFormat format) -+ { -+ // 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 = format != GeoFormat.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++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // get data -+ final short data = bb.getShort(); -+ -+ // add nswe and height -+ _temp.put((byte) (data & 0x000F)); -+ _temp.putShort((short) ((short) (data & 0xFFF0) >> 1)); -+ } -+ else -+ { -+ // add nswe -+ _temp.put(bb.get()); -+ -+ // add height -+ _temp.putShort(bb.getShort()); -+ } -+ } -+ } -+ -+ // 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 height -+ if (height > worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, return minimum value -+ return Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 height -+ if (height < worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, return maximum value -+ return Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 nswe -+ if (height > worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 nswe -+ if (height < worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @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 first layer data (first from bottom) -+ byte layers = _buffer[index++]; -+ -+ // loop though all cell layers, find closest layer -+ 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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ // get height -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(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]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ // get nswe -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ // set nswe -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_MULTILAYER_L2D); -+ -+ // for each cell -+ int index = 0; -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ // write layers count -+ final byte layers = _buffer[index++]; -+ stream.write(layers); -+ -+ // write cell data -+ stream.write(_buffer, index, layers * 3); -+ -+ // move index to next cell -+ index += layers * 3; -+ } -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (working copy) -@@ -0,0 +1,150 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockNull extends ABlock -+{ -+ private final byte _nswe; -+ -+ public BlockNull() -+ { -+ _nswe = (byte) 0xFF; -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return false; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweBelow(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) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) -+ { -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (nonexistent) -@@ -1,48 +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 . -- */ --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() -- { -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (nonexistent) -@@ -1,77 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (nonexistent) -@@ -1,56 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (working copy) -@@ -0,0 +1,39 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public enum GeoFormat -+{ -+ L2J("%d_%d.l2j"), -+ L2OFF("%d_%d_conv.dat"), -+ L2D("%d_%d.l2d"); -+ -+ private final String _filename; -+ -+ private GeoFormat(String filename) -+ { -+ _filename = filename; -+ } -+ -+ public String getFilename() -+ { -+ return _filename; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (working copy) -@@ -0,0 +1,67 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.geoengine.GeoEngine; -+import org.l2jmobius.gameserver.model.Location; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoLocation extends Location -+{ -+ private byte _nswe; -+ -+ public GeoLocation(int x, int y, int z) -+ { -+ super(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public void set(int x, int y, short z) -+ { -+ super.setXYZ(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public int getGeoX() -+ { -+ return _x; -+ } -+ -+ public int getGeoY() -+ { -+ return _y; -+ } -+ -+ @Override -+ public int getX() -+ { -+ return GeoEngine.getWorldX(_x); -+ } -+ -+ @Override -+ public int getY() -+ { -+ return GeoEngine.getWorldY(_y); -+ } -+ -+ public byte getNSWE() -+ { -+ return _nswe; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (working copy) -@@ -0,0 +1,70 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoStructure -+{ -+ // cells -+ public static final byte CELL_FLAG_E = 1 << 0; -+ public static final byte CELL_FLAG_W = 1 << 1; -+ public static final byte CELL_FLAG_S = 1 << 2; -+ public static final byte CELL_FLAG_N = 1 << 3; -+ public static final byte CELL_FLAG_SE = 1 << 4; -+ public static final byte CELL_FLAG_SW = 1 << 5; -+ public static final byte CELL_FLAG_NE = 1 << 6; -+ public static final byte CELL_FLAG_NW = (byte) (1 << 7); -+ -+ public static final int CELL_HEIGHT = 8; -+ public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6; -+ -+ // blocks -+ public static final byte TYPE_FLAT_L2J_L2OFF = 0; -+ public static final byte TYPE_FLAT_L2D = (byte) 0xD0; -+ public static final byte TYPE_COMPLEX_L2J = 1; -+ public static final byte TYPE_COMPLEX_L2OFF = 0x40; -+ public static final byte TYPE_COMPLEX_L2D = (byte) 0xD1; -+ 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) -+ public static final byte TYPE_MULTILAYER_L2D = (byte) 0xD2; -+ -+ 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; -+ -+ // regions -+ 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; -+ -+ // global geodata -+ private static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); -+ private 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; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (nonexistent) -@@ -1,42 +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 . -- */ --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); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (working copy) -@@ -0,0 +1,53 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public interface IGeoObject -+{ -+ /** -+ * Returns geodata X coordinate of the {@link IGeoObject}. -+ * @return int : Geodata X coordinate. -+ */ -+ int getGeoX(); -+ -+ /** -+ * Returns geodata Y coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Y coordinate. -+ */ -+ int getGeoY(); -+ -+ /** -+ * Returns geodata Z coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Z coordinate. -+ */ -+ int getGeoZ(); -+ -+ /** -+ * Returns height of the {@link IGeoObject}. -+ * @return int : Height. -+ */ -+ int getHeight(); -+ -+ /** -+ * Returns {@link IGeoObject} data. -+ * @return byte[][] : {@link IGeoObject} data. -+ */ -+ byte[][] getObjectGeoData(); -+} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (nonexistent) -@@ -1,47 +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 . -- */ --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(); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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); -- -- // 1 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (nonexistent) -@@ -1,55 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --/** -- * @author HorridoJoho -- */ --public final class NullRegion implements IRegion --{ -- public static final NullRegion INSTANCE = new NullRegion(); -- -- @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 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Region.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (nonexistent) -@@ -1,92 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (nonexistent) -@@ -1,87 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public abstract class AbstractNode --{ -- private T _loc; -- private AbstractNode _parent; -- -- public AbstractNode(T loc) -- { -- _loc = loc; -- } -- -- public void setParent(AbstractNode p) -- { -- _parent = p; -- } -- -- public AbstractNode getParent() -- { -- return _parent; -- } -- -- public T getLoc() -- { -- return _loc; -- } -- -- public void setLoc(T l) -- { -- _loc = l; -- } -- -- @Override -- public int hashCode() -- { -- final int prime = 31; -- int result = 1; -- result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); -- return result; -- } -- -- @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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (nonexistent) -@@ -1,33 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --/** -- * @author -Nemesiss- -- */ --public abstract class AbstractNodeLoc --{ -- public abstract int getX(); -- -- public abstract int getY(); -- -- public abstract int getZ(); -- -- public abstract int getNodeX(); -- -- public abstract int getNodeY(); --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (nonexistent) -@@ -1,67 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public class CellNode extends AbstractNode --{ -- 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (nonexistent) -@@ -1,348 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --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 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) -- { -- _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(); -- } -- -- public final List debugPath() -- { -- final List 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 void getNeighbors() -- { -- if (!_current.getLoc().canGoAll()) -- { -- 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); -- } -- -- // SouthEast -- if ((nodeE != null) && (nodeS != null)) -- { -- if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) -- { -- addNode(x + 1, y + 1, z, true); -- } -- } -- -- // SouthWest -- if ((nodeS != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) -- { -- addNode(x - 1, y + 1, z, true); -- } -- } -- -- // NorthEast -- if ((nodeN != null) && (nodeE != null)) -- { -- if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) -- { -- addNode(x + 1, y - 1, z, true); -- } -- } -- -- // NorthWest -- if ((nodeN != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) -- { -- addNode(x - 1, y - 1, z, true); -- } -- } -- } -- -- private 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(); -- // reinit node if needed -- if (result.getLoc() != null) -- { -- result.getLoc().set(x, y, z); -- } -- else -- { -- result.setLoc(new NodeLoc(x, y, z)); -- } -- } -- -- return result; -- } -- -- private 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 boolean isHighWeight(int x, int y, int z) -- { -- final CellNode result = getNode(x, y, z); -- if (result == null) -- { -- return true; -- } -- -- if (!result.getLoc().canGoAll()) -- { -- return true; -- } -- if (Math.abs(result.getLoc().getZ() - z) > 16) -- { -- return true; -- } -- -- return false; -- } -- -- private 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (working copy) -@@ -0,0 +1,87 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+ -+/** -+ * @author Hasha -+ */ -+public class Node -+{ -+ // node coords and nswe flag -+ private GeoLocation _loc; -+ -+ // node parent (for reverse path construction) -+ private Node _parent; -+ // node child (for moving over nodes during iteration) -+ private Node _child; -+ -+ // node G cost (movement cost = parent movement cost + current movement cost) -+ private double _cost = -1000; -+ -+ public void setLoc(int x, int y, int z) -+ { -+ _loc = new GeoLocation(x, y, z); -+ } -+ -+ public GeoLocation getLoc() -+ { -+ return _loc; -+ } -+ -+ public void setParent(Node parent) -+ { -+ _parent = parent; -+ } -+ -+ public Node getParent() -+ { -+ return _parent; -+ } -+ -+ public void setChild(Node child) -+ { -+ _child = child; -+ } -+ -+ public Node getChild() -+ { -+ return _child; -+ } -+ -+ public void setCost(double cost) -+ { -+ _cost = cost; -+ } -+ -+ public double getCost() -+ { -+ return _cost; -+ } -+ -+ public void free() -+ { -+ // reset node location -+ _loc = null; -+ -+ // reset node parent, child and cost -+ _parent = null; -+ _child = null; -+ _cost = -1000; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (working copy) -@@ -0,0 +1,306 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import java.util.concurrent.locks.ReentrantLock; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+ -+/** -+ * @author DS, Hasha; Credits to Diamond -+ */ -+public class NodeBuffer -+{ -+ private final ReentrantLock _lock = new ReentrantLock(); -+ private final int _size; -+ private final Node[][] _buffer; -+ -+ // center coordinates -+ private int _cx = 0; -+ private int _cy = 0; -+ -+ // target coordinates -+ private int _gtx = 0; -+ private int _gty = 0; -+ private short _gtz = 0; -+ -+ private Node _current = null; -+ -+ /** -+ * Constructor of NodeBuffer. -+ * @param size : one dimension size of buffer -+ */ -+ public NodeBuffer(int size) -+ { -+ // set size -+ _size = size; -+ -+ // initialize buffer -+ _buffer = new Node[size][size]; -+ for (int x = 0; x < size; x++) -+ { -+ for (int y = 0; y < size; y++) -+ { -+ _buffer[x][y] = 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 Node : first node of path -+ */ -+ public Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) -+ { -+ // set coordinates (middle of the line (gox,goy) - (gtx,gty), will be in the center of the buffer) -+ _cx = gox + ((gtx - gox - _size) / 2); -+ _cy = goy + ((gty - goy - _size) / 2); -+ -+ _gtx = gtx; -+ _gty = gty; -+ _gtz = gtz; -+ -+ _current = getNode(gox, goy, goz); -+ _current.setCost(getCostH(gox, goy, goz)); -+ -+ int count = 0; -+ do -+ { -+ // reached target? -+ if ((_current.getLoc().getGeoX() == _gtx) && (_current.getLoc().getGeoY() == _gty) && (Math.abs(_current.getLoc().getZ() - _gtz) < 8)) -+ { -+ return _current; -+ } -+ -+ // expand current node -+ expand(); -+ -+ // move pointer -+ _current = _current.getChild(); -+ } -+ while ((_current != null) && (count++ < Config.MAX_ITERATIONS)); -+ -+ return null; -+ } -+ -+ public boolean isLocked() -+ { -+ return _lock.tryLock(); -+ } -+ -+ public void free() -+ { -+ _current = null; -+ -+ for (Node[] nodes : _buffer) -+ { -+ for (Node node : nodes) -+ { -+ if (node.getLoc() != null) -+ { -+ node.free(); -+ } -+ } -+ } -+ -+ _lock.unlock(); -+ } -+ -+ /** -+ * Check _current Node and add its neighbors to the buffer. -+ */ -+ private final void expand() -+ { -+ // can't move anywhere, don't expand -+ final byte nswe = _current.getLoc().getNSWE(); -+ if (nswe == 0) -+ { -+ return; -+ } -+ -+ // get geo coords of the node to be expanded -+ final int x = _current.getLoc().getGeoX(); -+ final int y = _current.getLoc().getGeoY(); -+ final short z = (short) _current.getLoc().getZ(); -+ -+ // can move north, expand -+ if ((nswe & GeoStructure.CELL_FLAG_N) != 0) -+ { -+ addNode(x, y - 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move south, expand -+ if ((nswe & GeoStructure.CELL_FLAG_S) != 0) -+ { -+ addNode(x, y + 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_W) != 0) -+ { -+ addNode(x - 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_E) != 0) -+ { -+ addNode(x + 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move north-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NW) != 0) -+ { -+ addNode(x - 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move north-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NE) != 0) -+ { -+ addNode(x + 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SW) != 0) -+ { -+ addNode(x - 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SE) != 0) -+ { -+ addNode(x + 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ } -+ -+ /** -+ * Returns node, if it exists in buffer. -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param z : node Z coord -+ * @return Node : node, if exits in buffer -+ */ -+ private final Node getNode(int x, int y, short z) -+ { -+ // check node X out of coordinates -+ final int ix = x - _cx; -+ if ((ix < 0) || (ix >= _size)) -+ { -+ return null; -+ } -+ -+ // check node Y out of coordinates -+ final int iy = y - _cy; -+ if ((iy < 0) || (iy >= _size)) -+ { -+ return null; -+ } -+ -+ // get node -+ final Node result = _buffer[ix][iy]; -+ -+ // check and update -+ if (result.getLoc() == null) -+ { -+ result.setLoc(x, y, z); -+ } -+ -+ // return node -+ return result; -+ } -+ -+ /** -+ * Add node given by coordinates to the buffer. -+ * @param x : geo X coord -+ * @param y : geo Y coord -+ * @param z : geo Z coord -+ * @param weight : weight of movement to new node -+ */ -+ private final void addNode(int x, int y, short z, int weight) -+ { -+ // get node to be expanded -+ final Node node = getNode(x, y, z); -+ if (node == null) -+ { -+ return; -+ } -+ -+ // Z distance between nearby cells is higher than cell size -+ if (node.getLoc().getZ() > (z + (2 * GeoStructure.CELL_HEIGHT))) -+ { -+ return; -+ } -+ -+ // node was already expanded, return -+ if (node.getCost() >= 0) -+ { -+ return; -+ } -+ -+ node.setParent(_current); -+ if (node.getLoc().getNSWE() != (byte) 0xFF) -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + (weight * Config.OBSTACLE_MULTIPLIER)); -+ } -+ else -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + weight); -+ } -+ -+ Node current = _current; -+ int count = 0; -+ while ((current.getChild() != null) && (count < (Config.MAX_ITERATIONS * 4))) -+ { -+ count++; -+ if (current.getChild().getCost() > node.getCost()) -+ { -+ node.setChild(current.getChild()); -+ break; -+ } -+ current = current.getChild(); -+ } -+ -+ if (count >= (Config.MAX_ITERATIONS * 4)) -+ { -+ System.err.println("Pathfinding: too long loop detected, cost:" + node.getCost()); -+ } -+ -+ current.setChild(node); -+ } -+ -+ /** -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param i : node Z coord -+ * @return double : node cost -+ */ -+ private final double getCostH(int x, int y, int i) -+ { -+ final int dX = x - _gtx; -+ final int dY = y - _gty; -+ final int dZ = (i - _gtz) / GeoStructure.CELL_HEIGHT; -+ -+ // return (Math.abs(dX) + Math.abs(dY) + Math.abs(dZ)) * Config.HEURISTIC_WEIGHT; // Manhattan distance -+ return Math.sqrt((dX * dX) + (dY * dY) + (dZ * dZ)) * Config.HEURISTIC_WEIGHT; // Direct distance -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -- --/** -- * @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 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 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; -- // return super.hashCode(); -- } -- -- @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; -- if (_x != other._x) -- { -- return false; -- } -- if (_y != other._y) -- { -- return false; -- } -- if (_goNorth != other._goNorth) -- { -- return false; -- } -- if (_goEast != other._goEast) -- { -- return false; -- } -- if (_goSouth != other._goSouth) -- { -- return false; -- } -- if (_goWest != other._goWest) -- { -- return false; -- } -- if (_geoHeight != other._geoHeight) -- { -- return false; -- } -- return true; -- } --} -Index: java/org/l2jmobius/gameserver/model/actor/Creature.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/Creature.java (revision 8428) -+++ java/org/l2jmobius/gameserver/model/actor/Creature.java (working copy) -@@ -65,8 +65,6 @@ - import org.l2jmobius.gameserver.enums.TeleportWhereType; - import org.l2jmobius.gameserver.enums.UserInfoType; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; - import org.l2jmobius.gameserver.instancemanager.IdManager; - import org.l2jmobius.gameserver.instancemanager.MapRegionManager; - import org.l2jmobius.gameserver.instancemanager.QuestManager; -@@ -2566,7 +2564,7 @@ - - public boolean disregardingGeodata; - public int onGeodataPathIndex; -- public List geoPath; -+ public List geoPath; - public int geoPathAccurateTx; - public int geoPathAccurateTy; - public int geoPathGtx; -@@ -3443,7 +3441,7 @@ - if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) - { - // Path calculation -- overrides previous movement check -- m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); -+ m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); - if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found - { - if (isPlayer() && !_isFlying && !isInWater) -Index: java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (revision 8424) -+++ java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (working copy) -@@ -12746,7 +12746,7 @@ - { - if (Config.CORRECT_PLAYER_Z) - { -- final int nearestZ = GeoEngine.getInstance().getHigherHeight(getX(), getY(), getZ()); -+ final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); - if (getZ() < nearestZ) - { - teleToLocation(new Location(getX(), getY(), nearestZ)); -Index: java/org/l2jmobius/gameserver/util/GeoUtils.java -=================================================================== ---- java/org/l2jmobius/gameserver/util/GeoUtils.java (revision 8409) -+++ java/org/l2jmobius/gameserver/util/GeoUtils.java (working copy) -@@ -19,7 +19,7 @@ - import java.awt.Color; - - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; - import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; - -@@ -26,25 +26,25 @@ - /** - * @author HorridoJoho - */ --public final class GeoUtils -+public class GeoUtils - { - public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); - - final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); - - while (iter.next()) - { -- final int wx = GeoEngine.getInstance().getWorldX(iter.x()); -- final int wy = GeoEngine.getInstance().getWorldY(iter.y()); -+ final int wx = GeoEngine.getWorldX(iter.x()); -+ final int wy = GeoEngine.getWorldY(iter.y()); - - prim.addPoint(Color.RED, wx, wy, z); - } -@@ -53,21 +53,21 @@ - - public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); -- int wy = GeoEngine.getInstance().getWorldY(prevY); -+ int wx = GeoEngine.getWorldX(prevX); -+ int wy = GeoEngine.getWorldY(prevY); - int wz = iter.z(); - prim.addPoint(Color.RED, wx, wy, wz); - -@@ -78,8 +78,8 @@ - - if ((curX != prevX) || (curY != prevY)) - { -- wx = GeoEngine.getInstance().getWorldX(curX); -- wy = GeoEngine.getInstance().getWorldY(curY); -+ wx = GeoEngine.getWorldX(curX); -+ wy = GeoEngine.getWorldY(curY); - wz = iter.z(); - - prim.addPoint(Color.RED, wx, wy, wz); -@@ -93,7 +93,7 @@ - - private static Color getDirectionColor(int x, int y, int z, int nswe) - { -- if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) -+ if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) - { - return Color.GREEN; - } -@@ -109,9 +109,8 @@ - int iPacket = 0; - - ExServerPrimitive exsp = null; -- final GeoEngine ge = GeoEngine.getInstance(); -- final int playerGx = ge.getGeoX(player.getX()); -- final int playerGy = ge.getGeoY(player.getY()); -+ final int playerGx = GeoEngine.getGeoX(player.getX()); -+ final int playerGy = GeoEngine.getGeoY(player.getY()); - for (int dx = -geoRadius; dx <= geoRadius; ++dx) - { - for (int dy = -geoRadius; dy <= geoRadius; ++dy) -@@ -135,12 +134,12 @@ - final int gx = playerGx + dx; - final int gy = playerGy + dy; - -- final int x = ge.getWorldX(gx); -- final int y = ge.getWorldY(gy); -- final int z = ge.getNearestZ(gx, gy, player.getZ()); -+ final int x = GeoEngine.getWorldX(gx); -+ final int y = GeoEngine.getWorldY(gy); -+ final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); - - // north arrow -- Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); -+ Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); - 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); -@@ -147,7 +146,7 @@ - exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z); - - // east arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); - 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); -@@ -154,13 +153,13 @@ - exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z); - - // south arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); - 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, Cell.NSWE_WEST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); - 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); -@@ -188,15 +187,15 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_EAST; -+ return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_EAST; -+ return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; - } - else - { -- return Cell.NSWE_EAST; -+ return GeoStructure.CELL_FLAG_E; // Direction.EAST; - } - } - else if (x < lastX) // west -@@ -203,26 +202,27 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_WEST; -+ return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_WEST; -+ return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; - } - else - { -- return Cell.NSWE_WEST; -+ return GeoStructure.CELL_FLAG_W; // Direction.WEST; - } - } -- else // unchanged x -+ else -+ // unchanged x - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH; -+ return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH; -+ return GeoStructure.CELL_FLAG_N; // Direction.NORTH; - } - else - { -Index: java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java -=================================================================== ---- java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (nonexistent) -+++ java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (working copy) -@@ -0,0 +1,386 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.tools.geodataconverter; -+ -+import java.io.BufferedOutputStream; -+import java.io.File; -+import java.io.FileOutputStream; -+import java.io.RandomAccessFile; -+import java.nio.ByteOrder; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.Scanner; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.commons.util.PropertiesParser; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoDataConverter -+{ -+ private static GeoFormat _format; -+ private static ABlock[][] _blocks; -+ -+ public static void main(String[] args) -+ { -+ // initialize config -+ loadGeoengineConfigs(); -+ -+ // get geodata format -+ String type = ""; -+ try (Scanner scn = new Scanner(System.in)) -+ { -+ while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) -+ { -+ System.out.println("GeoDataConverter: Select source geodata type:"); -+ System.out.println(" J: L2J (e.g. 23_22.l2j)"); -+ System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); -+ System.out.println(" E: Exit"); -+ System.out.print("Choice: "); -+ type = scn.next(); -+ } -+ } -+ if (type.equalsIgnoreCase("E")) -+ { -+ System.exit(0); -+ } -+ -+ _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; -+ -+ // start conversion -+ System.out.println("GeoDataConverter: Converting all " + _format + " files."); -+ -+ // initialize geodata container -+ _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files -+ int converted = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) -+ { -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) -+ { -+ final String input = String.format(_format.getFilename(), rx, ry); -+ final String filepath = Config.GEODATA_PATH; -+ final File f = new File(filepath + input); -+ if (f.exists() && !f.isDirectory()) -+ { -+ // load geodata -+ if (!loadGeoBlocks(input)) -+ { -+ System.out.println("GeoDataConverter: Unable to load " + input + " region file."); -+ continue; -+ } -+ -+ // recalculate nswe -+ if (!recalculateNswe()) -+ { -+ System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); -+ continue; -+ } -+ -+ // save geodata -+ final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); -+ if (!saveGeoBlocks(output)) -+ { -+ System.out.println("GeoDataConverter: Unable to save " + output + " region file."); -+ continue; -+ } -+ -+ converted++; -+ System.out.println("GeoDataConverter: Created " + output + " region file."); -+ } -+ } -+ } -+ System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); -+ } -+ -+ /** -+ * Loads geo blocks from buffer of the region file. -+ * @param filename : The name of the to load. -+ * @return boolean : True when successful. -+ */ -+ private static boolean loadGeoBlocks(String filename) -+ { -+ // region file is load-able, try to load it -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) -+ { -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) -+ if (_format == GeoFormat.L2OFF) -+ { -+ for (int i = 0; i < 18; i++) -+ { -+ buffer.get(); -+ } -+ } -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ if (_format == GeoFormat.L2J) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2J: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2J: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ else -+ { -+ // get block type -+ final short type = buffer.getShort(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ default: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ if (buffer.remaining() > 0) -+ { -+ System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ return false; -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); -+ return false; -+ } -+ } -+ -+ /** -+ * Recalculate diagonal flags for the region file. -+ * @return boolean : True when successful. -+ */ -+ private static boolean recalculateNswe() -+ { -+ try -+ { -+ for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) -+ { -+ for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) -+ { -+ // get block -+ final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // skip flat blocks -+ if (block instanceof BlockFlat) -+ { -+ continue; -+ } -+ -+ // for complex and multilayer blocks go though all layers -+ short height = Short.MAX_VALUE; -+ int index; -+ while ((index = block.getIndexBelow(x, y, height)) != -1) -+ { -+ // get height and nswe -+ height = block.getHeight(index); -+ byte nswe = block.getNswe(index); -+ -+ // update nswe with diagonal flags -+ nswe = updateNsweBelow(x, y, height, nswe); -+ -+ // set nswe of the cell -+ block.setNswe(index, nswe); -+ } -+ } -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ /** -+ * Updates the NSWE flag with diagonal flags. -+ * @param x : Geodata X coordinate. -+ * @param y : Geodata Y coordinate. -+ * @param z : Geodata Z coordinate. -+ * @param nsweValue : NSWE flag to be updated. -+ * @return byte : Updated NSWE flag. -+ */ -+ private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) -+ { -+ byte nswe = nsweValue; -+ -+ // calculate virtual layer height -+ final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); -+ -+ // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) -+ final byte nsweN = getNsweBelow(x, y - 1, height); -+ final byte nsweS = getNsweBelow(x, y + 1, height); -+ final byte nsweW = getNsweBelow(x - 1, y, height); -+ final byte nsweE = getNsweBelow(x + 1, y, height); -+ -+ // north-west -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NW; -+ } -+ -+ // north-east -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NE; -+ } -+ -+ // south-west -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SW; -+ } -+ -+ // south-east -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SE; -+ } -+ -+ return nswe; -+ } -+ -+ private static byte getNsweBelow(int geoX, int geoY, short worldZ) -+ { -+ // out of geo coordinates -+ if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) -+ { -+ return 0; -+ } -+ -+ // out of geo coordinates -+ if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) -+ { -+ return 0; -+ } -+ -+ // get block -+ final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // get index, when valid, return nswe -+ final int index = block.getIndexBelow(geoX, geoY, worldZ); -+ return index == -1 ? 0 : block.getNswe(index); -+ } -+ -+ /** -+ * Save region file to file. -+ * @param filename : The name of file to save. -+ * @return boolean : True when successful. -+ */ -+ private static boolean saveGeoBlocks(String filename) -+ { -+ try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) -+ { -+ // loop over region blocks and save each block -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[ix][iy].saveBlock(bos); -+ } -+ } -+ -+ // flush data to file -+ bos.flush(); -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ private static void loadGeoengineConfigs() -+ { -+ final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); -+ Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); -+ Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); -+ Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); -+ Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); -+ Config.PATHFINDING = geoData.getBoolean("PathFinding", true); -+ Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -+ Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); -+ Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); -+ Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); -+ Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); -+ Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); -+ } -+} -\ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/geodata/Readme.txt b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/geodata/Readme.txt index a480c8c380..2611882e56 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/geodata/Readme.txt +++ b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/geodata/Readme.txt @@ -23,8 +23,8 @@ a - Prerequisites b - Make it work ---------------------------------------------- -To make geodata working: -* unpack your geodata files into "/data/geodata" folder -* open "/config/GeoEngine.ini" with your favorite text editor and then edit following config: - - CoordSynchronize = 2 -* If you do not use any geodata files, the server will automatically change this setting to -1. +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. diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/ai/others/FleeMonsters.java b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/ai/others/FleeMonsters.java index 4562336103..a971742033 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/ai/others/FleeMonsters.java +++ b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/ai/others/FleeMonsters.java @@ -59,7 +59,7 @@ public class FleeMonsters extends AbstractNpcAI final int posX = (int) (npc.getX() + (FLEE_DISTANCE * Math.cos(radians))); final int posY = (int) (npc.getY() + (FLEE_DISTANCE * Math.sin(radians))); final int posZ = npc.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, attacker.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); return super.onAttack(npc, attacker, damage, isSummon); } diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/ai/others/RandomWalkingGuards.java b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/ai/others/RandomWalkingGuards.java index b99becfdc6..780502d5d6 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/ai/others/RandomWalkingGuards.java +++ b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/ai/others/RandomWalkingGuards.java @@ -55,7 +55,7 @@ public class RandomWalkingGuards extends AbstractNpcAI if (!npc.isInCombat()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, Config.MAX_DRIFT_RANGE); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("RANDOM_WALK", getRandom(MIN_WALK_DELAY, MAX_WALK_DELAY), npc, null); } diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index 524652cbc1..18511c6541 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { @@ -133,8 +133,8 @@ public class AdminGeodata implements IAdminCommandHandler } case "admin_geomap": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y + " (" + ((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + "," + ((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + " to " + ((((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + "," + ((((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + ")"); break; } diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java index f3dffa1227..76572f7f25 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java +++ b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java @@ -57,8 +57,8 @@ public class AdminMissingHtmls implements IAdminCommandHandler { case "admin_geomap_missing_htmls": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final int topLeftX = (x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE; final int topLeftY = (y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE; final int bottomRightX = (((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1; diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index fdb2319138..a0a34f1051 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -19,9 +19,9 @@ package handlers.admincommandhandlers; import java.util.List; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; +import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.util.BuilderUtil; @@ -44,13 +44,13 @@ public class AdminPathNode implements IAdminCommandHandler } if (activeChar.getTarget() != null) { - final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { BuilderUtil.sendSysMessage(activeChar, "No Route!"); return true; } - for (AbstractNodeLoc a : path) + for (Location a : path) { BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/Blink.java b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/Blink.java index 22f2c84eae..22697e8e20 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/Blink.java +++ b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/Blink.java @@ -88,7 +88,7 @@ public class Blink extends AbstractEffect final int y = effected.getY() + y1; final int z = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, destination, _flyType, _flySpeed, _flyDelay, _animationSpeed)); diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/Fear.java b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/Fear.java index 95a5afdf98..88c795e04b 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/Fear.java +++ b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/Fear.java @@ -100,7 +100,7 @@ public class Fear extends AbstractEffect final int posY = (int) (effected.getY() + (FEAR_RANGE * Math.sin(radians))); final int posZ = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); } } diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java index fddd94c60a..55c2e78d8a 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java +++ b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java @@ -57,7 +57,7 @@ public class FlyAway extends AbstractEffect final int y = (int) (effector.getY() - (nRadius * (dy / distance))); final int z = effector.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.broadcastPacket(new FlyToLocation(effected, destination, FlyType.THROW_UP)); effected.setXYZ(destination); diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java index 1fbd8239b0..89ad8670dc 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java +++ b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java @@ -133,7 +133,7 @@ public class KnockBack extends AbstractEffect final int x = (int) (effected.getX() + (_distance * Math.cos(radians))); final int y = (int) (effected.getY() + (_distance * Math.sin(radians))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, loc, _type, _speed, _delay, _animationSpeed)); diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/PullBack.java b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/PullBack.java index ed6a37eff6..38fe2fe9d0 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/PullBack.java +++ b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/PullBack.java @@ -73,7 +73,7 @@ public class PullBack extends AbstractEffect } // In retail, you get debuff, but you are not even moved if there is obstacle. You are still disabled from using skills and moving though. - if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effector.getInstanceWorld())) + if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effected.getInstanceWorld())) { effected.broadcastPacket(new FlyToLocation(effected, effector, _type, _speed, _delay, _animationSpeed)); effected.setXYZ(effector.getX(), effector.getY(), GeoEngine.getInstance().getHeight(effector.getX(), effector.getY(), effector.getZ()) + 10); diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java index d6f9664141..813e496c47 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java +++ b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java @@ -87,7 +87,7 @@ public class TeleportToSummon extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = summon.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java index d3c263978c..c9dc34ac22 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java +++ b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java @@ -76,7 +76,7 @@ public class TeleportToTarget extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java index f2148adb54..c7ca6f64cb 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java +++ b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java @@ -65,7 +65,7 @@ public class RollingDice implements IItemHandler final int x = player.getX() + x1; final int y = player.getY() + y1; final int z = player.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); Broadcast.toSelfAndKnownPlayers(player, new Dice(player.getObjectId(), itemId, number, destination.getX(), destination.getY(), destination.getZ())); final SystemMessage sm = new SystemMessage(SystemMessageId.C1_HAS_ROLLED_A_S2); diff --git a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/targethandlers/Ground.java b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/targethandlers/Ground.java index 82ab321c29..d8c73627f5 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/targethandlers/Ground.java +++ b/L2J_Mobius_Classic_2.1_Zaken/dist/game/data/scripts/handlers/targethandlers/Ground.java @@ -52,7 +52,7 @@ public class Ground implements ITargetTypeHandler return null; } - if (!GeoEngine.getInstance().canSeeTarget(creature, worldPosition)) + if (!GeoEngine.getInstance().canSeeLocation(creature, worldPosition)) { if (sendMessage) { diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/Config.java index 356cab0845..b3cc117028 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/Config.java @@ -61,6 +61,7 @@ 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; @@ -893,12 +894,17 @@ public class Config // GeoEngine // -------------------------------------------------- public static Path GEODATA_PATH; + public static GeoType GEODATA_TYPE; public static boolean PATHFINDING; public static String PATHFIND_BUFFERS; - public static float LOW_WEIGHT; - public static float MEDIUM_WEIGHT; - public static float HIGH_WEIGHT; - public static float DIAGONAL_WEIGHT; + public static int MOVE_WEIGHT; + public static int MOVE_WEIGHT_DIAG; + public static int OBSTACLE_WEIGHT; + 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; /** Attribute System */ public static int S_WEAPON_STONE; @@ -2392,12 +2398,17 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); + GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); - MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); - HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); - DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); + MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); + OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); + HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); + MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); + MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); // Load AllowedPlayerRaces config file (if exists) final PropertiesParser AllowedPlayerRaces = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE); diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/commons/util/CommonUtil.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/commons/util/CommonUtil.java index 14ae3a4560..ab7837d827 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/commons/util/CommonUtil.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/commons/util/CommonUtil.java @@ -584,4 +584,15 @@ public class CommonUtil final DecimalFormat formatter = new DecimalFormat(format, new DecimalFormatSymbols(Locale.ENGLISH)); return formatter.format(value); } + + /** + * @param numToTest : The number to test. + * @param min : The minimum limit. + * @param max : The maximum limit. + * @return the number or one of the limit (mininum / maximum). + */ + public static int limit(int numToTest, int min, int max) + { + return (numToTest > max) ? max : ((numToTest < min) ? min : numToTest); + } } diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/commons/util/Point2D.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/commons/util/Point2D.java new file mode 100644 index 0000000000..ebb6272e5e --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/commons/util/Point2D.java @@ -0,0 +1,180 @@ +/* + * 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 . + */ +package org.l2jmobius.commons.util; + +import java.util.Objects; + +/** + * A datatype used to retain a 2D (x/y) point. It got the capability to be set and cleaned. + */ +public class Point2D +{ + protected volatile int _x; + protected volatile int _y; + + public Point2D(int x, int y) + { + _x = x; + _y = y; + } + + @Override + public Point2D clone() + { + return new Point2D(_x, _y); + } + + @Override + public String toString() + { + return _x + ", " + _y; + } + + @Override + public int hashCode() + { + return Objects.hash(_x, _y); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final Point2D other = (Point2D) obj; + return (_x == other._x) && (_y == other._y); + } + + /** + * @param x : The X coord to test. + * @param y : The Y coord to test. + * @return True if all coordinates equals this {@link Point2D} coordinates. + */ + public boolean equals(int x, int y) + { + return (_x == x) && (_y == y); + } + + public int getX() + { + return _x; + } + + public void setX(int x) + { + _x = x; + } + + public int getY() + { + return _y; + } + + public void setY(int y) + { + _y = y; + } + + public void set(int x, int y) + { + _x = x; + _y = y; + } + + /** + * Refresh the current {@link Point2D} using a reference {@link Point2D} and a distance. The new destination is calculated to go in opposite side of the {@link Point2D} reference.
+ *
+ * This method is perfect to calculate fleeing characters position. + * @param referenceLoc : The Point2D used as position. + * @param distance : The distance to be set between current and new position. + */ + public void setFleeing(Point2D referenceLoc, int distance) + { + final double xDiff = referenceLoc.getX() - _x; + final double yDiff = referenceLoc.getY() - _y; + + final double yxRation = Math.abs(xDiff / yDiff); + + final int y = (int) (distance / (yxRation + 1)); + final int x = (int) (y * yxRation); + + _x += (xDiff < 0 ? x : -x); + _y += (yDiff < 0 ? y : -y); + } + + public void clean() + { + _x = 0; + _y = 0; + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @return The distance between this {@Point2D} and some given coordinates. + */ + public double distance2D(int x, int y) + { + final double dx = (double) _x - x; + final double dy = (double) _y - y; + + return Math.sqrt((dx * dx) + (dy * dy)); + } + + /** + * @param point : The {@link Point2D} to test. + * @return The distance between this {@Point2D} and the {@link Point2D} set as parameter. + */ + public double distance2D(Point2D point) + { + return distance2D(point.getX(), point.getY()); + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of some given coordinates. + */ + public boolean isIn2DRadius(int x, int y, int radius) + { + return distance2D(x, y) < radius; + } + + /** + * @param point : The Point2D to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of the {@link Point2D} set as parameter. + */ + public boolean isIn2DRadius(Point2D point, int radius) + { + return distance2D(point) < radius; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/AttackableAI.java index 547963e3e8..dc29d4cf6e 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/AttackableAI.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/ai/AttackableAI.java @@ -578,7 +578,7 @@ public class AttackableAI extends CreatureAI } // Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast) - final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); + final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); moveTo(moveLoc.getX(), moveLoc.getY(), moveLoc.getZ()); } } @@ -801,7 +801,7 @@ public class AttackableAI extends CreatureAI final int newZ = npc.getZ() + 30; // Mobius: Verify destination. Prevents wall collision issues and fixes monsters not avoiding obstacles. - moveTo(GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); + moveTo(GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); } return; } diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/data/SpawnTable.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/data/SpawnTable.java index a81201f69e..19f0db5b3e 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/data/SpawnTable.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/data/SpawnTable.java @@ -114,8 +114,8 @@ public class SpawnTable } // XML file for spawn - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); // Write info to XML @@ -192,8 +192,8 @@ public class SpawnTable if (update) { - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = spawn.getNpcSpawnTemplate() != null ? spawn.getNpcSpawnTemplate().getSpawnTemplate().getFile() : new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); final File tempFile = new File(spawnFile.getAbsolutePath().substring(Config.DATAPACK_ROOT.getAbsolutePath().length() + 1).replace('\\', '/') + ".tmp"); try diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/enums/GeoType.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/enums/GeoType.java new file mode 100644 index 0000000000..f77aa5eac4 --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/enums/GeoType.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.enums; + +public enum GeoType +{ + L2J("%d_%d.l2j"), + L2OFF("%d_%d_conv.dat"); + + private final String _filename; + + private GeoType(String filename) + { + _filename = filename; + } + + public String getFilename() + { + return _filename; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java new file mode 100644 index 0000000000..e62f50a8df --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java @@ -0,0 +1,144 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 9d255a5f54..7bef9c770b 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,72 +16,63 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.channels.FileChannel.MapMode; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.logging.Level; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; import java.util.logging.Logger; import org.l2jmobius.Config; +import org.l2jmobius.commons.util.CommonUtil; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; -import org.l2jmobius.gameserver.geoengine.geodata.IRegion; -import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; -import org.l2jmobius.gameserver.geoengine.geodata.Region; +import org.l2jmobius.gameserver.enums.GeoType; +import org.l2jmobius.gameserver.enums.MoveDirectionType; +import org.l2jmobius.gameserver.geoengine.geodata.ABlock; +import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; +import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; +import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; +import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.model.interfaces.ILocational; -import org.l2jmobius.gameserver.util.GeoUtils; -import org.l2jmobius.gameserver.util.LinePointIterator; -import org.l2jmobius.gameserver.util.LinePointIterator3D; -/** - * @author -Nemesiss-, HorridoJoho - */ public class GeoEngine { - private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - - private static final int WORLD_MIN_X = -655360; - private static final int WORLD_MIN_Y = -589824; - private static final int WORLD_MIN_Z = -16384; - - /** 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; - - /** The regions array */ - private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; - private static final int ELEVATED_SEE_OVER_DISTANCE = 2; - private static final int MAX_SEE_OVER_HEIGHT = 48; - protected GeoEngine() + private final ABlock[][] _blocks; + private final BlockNull _nullBlock; + + // Pre-allocated buffers. + private final BufferHolder[] _buffers; + + public GeoEngine() { LOGGER.info("GeoEngine: Initializing..."); - for (int i = 0; i < _regions.length(); i++) - { - _regions.set(i, NullRegion.INSTANCE); - } + // Initialize block container. + _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; + + // Load null block. + _nullBlock = new BlockNull(); + + // Initialize multilayer temporarily buffer. + BlockMultilayer.initialize(); + + // Load geo files according to geoengine config setup. int loaded = 0; try { @@ -92,23 +83,52 @@ public class GeoEngine final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); if (Files.exists(geoFilePath)) { - try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + // Region file is load-able, try to load it. + if (loadGeoBlocks(regionX, regionY)) { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); loaded++; } } + else + { + // Region file is not load-able, load null blocks. + loadNullBlocks(regionX, regionY); + } } } } catch (Exception e) { - LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + LOGGER.warning("GeoEngine: Failed to load geodata! " + e); System.exit(1); } - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + // Release multilayer block temporarily buffer. + BlockMultilayer.release(); + + String[] array = Config.PATHFIND_BUFFERS.split(";"); + _buffers = new BufferHolder[array.length]; + + int count = 0; + for (int i = 0; i < array.length; i++) + { + String buf = array[i]; + String[] args = buf.split("x"); + + try + { + int size = Integer.parseInt(args[1]); + count += size; + _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); + } + catch (Exception e) + { + LOGGER.warning("Could not load buffer setting:" + buf + ". " + e); + } + } + LOGGER.info("Loaded " + count + " node buffers."); + // Avoid wrong configs when no files are loaded. if ((loaded == 0) && Config.PATHFINDING) { @@ -118,616 +138,913 @@ public class GeoEngine } /** - * @param geoX - * @param geoY - * @return the region + * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer and log this situation. + * @param size : pre-calculated minimal required size + * @return NodeBuffer : buffer */ - private IRegion getRegion(int geoX, int geoY) + private NodeBuffer getBuffer(int size) { - final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); - if ((region < 0) || (region >= _regions.length())) + NodeBuffer current = null; + for (BufferHolder holder : _buffers) { - return null; - } - return _regions.get(region); - } - - /** - * @param filePath - * @param regionX - * @param regionY - * @throws IOException - */ - 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()).load().order(ByteOrder.LITTLE_ENDIAN))); - } - } - - /** - * @param regionX - * @param regionY - */ - public void unloadRegion(int regionX, int regionY) - { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); - } - - /** - * @param geoX - * @param geoY - * @return if geodata exist - */ - public boolean hasGeoPos(int geoX, int geoY) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return false; - } - return region.hasGeo(); - } - - /** - * Checks the specified position for available geodata. - * @param x the world x - * @param y the world y - * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise - */ - public boolean hasGeo(int x, int y) - { - return hasGeoPos(getGeoX(x), getGeoY(y)); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe check - */ - public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return true; - } - return region.checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe anti-corner cut - */ - public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) - { - boolean can = true; - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - return can && checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the nearest Z value - */ - public int getNearestZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNearestZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next lower Z value - */ - public int getNextLowerZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextLowerZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next higher Z value - */ - public int getNextHigherZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextHigherZ(geoX, geoY, worldZ); - } - - /** - * Gets the Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHeight(int x, int y, int z) - { - return getNearestZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next lower Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getLowerHeight(int x, int y, int z) - { - return getNextLowerZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next higher Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHigherHeight(int x, int y, int z) - { - return getNextHigherZ(getGeoX(x), getGeoY(y), z); - } - - /** - * @param worldX - * @return the geo X - */ - public int getGeoX(int worldX) - { - return (worldX - WORLD_MIN_X) / 16; - } - - /** - * @param worldY - * @return the geo Y - */ - public int getGeoY(int worldY) - { - return (worldY - WORLD_MIN_Y) / 16; - } - - /** - * @param worldZ - * @return the geo Z - */ - public int getGeoZ(int worldZ) - { - return (worldZ - WORLD_MIN_Z) / 16; - } - - /** - * @param geoX - * @return the world X - */ - public int getWorldX(int geoX) - { - return (geoX * 16) + WORLD_MIN_X + 8; - } - - /** - * @param geoY - * @return the world Y - */ - public int getWorldY(int geoY) - { - return (geoY * 16) + WORLD_MIN_Y + 8; - } - - /** - * @param geoZ - * @return the world Z - */ - public int getWorldZ(int geoZ) - { - return (geoZ * 16) + WORLD_MIN_Z + 8; - } - - /** - * Can see target. Doors as target always return true. Checks doors between. - * @param cha the character - * @param target the target - * @return {@code true} if the character can see the target (LOS), {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, WorldObject target) - { - if (target.isDoor()) - { - // Can always see doors. - return true; - } - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); - } - - /** - * Can see target. Checks doors between. - * @param cha the character - * @param worldPosition the world position - * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) - { - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param world - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @param tworld the target's instanceId - * @return - */ - public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) - { - return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instance - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) - { - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) - { - return false; - } - return canSeeTarget(x, y, z, tx, ty, tz); - } - - /** - * @param prevX - * @param prevY - * @param prevGeoZ - * @param curX - * @param curY - * @param nswe - * @return the LOS Z value - */ - private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) - { - if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) - { - throw new RuntimeException("Multiple directions!"); - } - - if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) - { - return getNearestZ(curX, curY, prevGeoZ); - } - - return getNextHigherZ(curX, curY, prevGeoZ); - } - - /** - * Can see target. Does not check doors between. - * @param xValue the x coordinate - * @param yValue the y coordinate - * @param zValue the z coordinate - * @param txValue the target's x coordinate - * @param tyValue the target's y coordinate - * @param tzValue the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) - { - int x = xValue; - int y = yValue; - int tx = txValue; - int ty = tyValue; - - int geoX = getGeoX(x); - int geoY = getGeoY(y); - int tGeoX = getGeoX(tx); - int tGeoY = getGeoY(ty); - - int z = getNearestZ(geoX, geoY, zValue); - int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - // Fastpath. - if ((geoX == tGeoX) && (geoY == tGeoY)) - { - if (hasGeoPos(tGeoX, tGeoY)) - { - return z == tz; - } - return true; - } - - if (tz > z) - { - int tmp = tx; - tx = x; - x = tmp; - - tmp = ty; - ty = y; - y = tmp; - - tmp = tz; - tz = z; - z = tmp; - - tmp = tGeoX; - tGeoX = geoX; - geoX = tmp; - - tmp = tGeoY; - tGeoY = geoY; - geoY = tmp; - } - - final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); - // First point is guaranteed to be available, skip it, we can always see our own position. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - final int prevZ = pointIter.z(); - int prevGeoZ = prevZ; - int ptIndex = 0; - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - - if ((curX == prevX) && (curY == prevY)) + // Find proper size of buffer. + if (holder._size < size) { continue; } - final int beeCurZ = pointIter.z(); - int curGeoZ = prevGeoZ; - - // Check if the position has geodata. - if (hasGeoPos(curX, curY)) + // Find unlocked NodeBuffer. + for (NodeBuffer buffer : holder._buffer) { - final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); - curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); - int maxHeight; - if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) + if (!buffer.isLocked()) { - maxHeight = z + MAX_SEE_OVER_HEIGHT; - } - else - { - maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; + continue; } - boolean canSeeThrough = false; - if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) + return buffer; + } + + LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); + + // NodeBuffer not found, allocate temporary buffer. + current = new NodeBuffer(holder._size); + current.isLocked(); + } + + return current; + } + + /** + * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + * @return boolean : True, when geodata file was loaded without problem. + */ + private boolean loadGeoBlocks(int regionX, int regionY) + { + final String filename = String.format(Config.GEODATA_TYPE.getFilename(), regionX, regionY); + final String filepath = Config.GEODATA_PATH + "\\" + filename; + + // Standard load. + try (RandomAccessFile raf = new RandomAccessFile(filepath, "r"); + FileChannel fc = raf.getChannel()) + { + // Initialize file buffer. + MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // Load 18B header for L2OFF geodata (1st and 2nd byte...region X and Y). + if (Config.GEODATA_TYPE == GeoType.L2OFF) + { + for (int i = 0; i < 18; i++) { - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) + buffer.get(); + } + } + + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Loop over region blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + if (Config.GEODATA_TYPE == GeoType.L2J) { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) - { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); + // Get block type. + final byte type = buffer.get(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + case GeoStructure.TYPE_MULTILAYER_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + default: + { + throw new IllegalArgumentException("Unknown block type: " + type); + } + } } else { - canSeeThrough = true; + // Get block type. + final short type = buffer.getShort(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + default: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + } } } + } + + // Check data consistency. + if (buffer.remaining() > 0) + { + LOGGER.warning("Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); + } + + // Loading was successful. + return true; + } + catch (Exception e) + { + // An error occured while loading, load null blocks. + LOGGER.warning("Error loading " + filename + " region file. " + e); + + // Replace whole region file with null blocks. + loadNullBlocks(regionX, regionY); + + // Loading was not successful. + return false; + } + } + + /** + * Loads null blocks. Used when no region file is detected or an error occurs during loading. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + */ + private void loadNullBlocks(int regionX, int regionY) + { + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Load all null blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + _blocks[blockX + ix][blockY + iy] = _nullBlock; + } + } + } + + /** + * Converts world X to geodata X. + * @param worldX + * @return int : Geo X + */ + public static int getGeoX(int worldX) + { + return (worldX - World.WORLD_X_MIN) >> 4; + } + + /** + * Converts world Y to geodata Y. + * @param worldY + * @return int : Geo Y + */ + public static int getGeoY(int worldY) + { + return (worldY - World.WORLD_Y_MIN) >> 4; + } + + /** + * Converts geodata X to world X. + * @param geoX + * @return int : World X + */ + public static int getWorldX(int geoX) + { + return (geoX << 4) + World.WORLD_X_MIN + 8; + } + + /** + * Converts geodata Y to world Y. + * @param geoY + * @return int : World Y + */ + public static int getWorldY(int geoY) + { + return (geoY << 4) + World.WORLD_Y_MIN + 8; + } + + /** + * Returns block of geodata on given coordinates. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return {@link ABlock} : Block of geodata. + */ + public ABlock getBlock(int geoX, int geoY) + { + final int x = geoX / GeoStructure.BLOCK_CELLS_X; + if ((x < 0) || (x >= GeoStructure.GEO_BLOCKS_X)) + { + return null; + } + final int y = geoY / GeoStructure.BLOCK_CELLS_Y; + if ((y < 0) || (y >= GeoStructure.GEO_BLOCKS_Y)) + { + return null; + } + return _blocks[x][y]; + } + + /** + * Check if geo coordinates has geo. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return boolean : True, if given geo coordinates have geodata + */ + public boolean hasGeoPos(int geoX, int geoY) + { + final ABlock block = getBlock(geoX, geoY); + return (block != null) && block.hasGeoPos(); + } + + /** + * Returns the height of cell, which is closest to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell geodata Z coordinate, closest to given coordinates. + */ + public short getHeightNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return (short) worldZ; + } + return block.getHeightNearest(geoX, geoY, worldZ); + } + + /** + * Returns the NSWE flag byte of cell, which is closes to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. + */ + public byte getNsweNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return GeoStructure.CELL_FLAG_ALL; + } + return block.getNsweNearest(geoX, geoY, worldZ); + } + + /** + * Check if world coordinates has geo. + * @param worldX : World X + * @param worldY : World Y + * @return boolean : True, if given world coordinates have geodata + */ + public boolean hasGeo(int worldX, int worldY) + { + return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param loc : The location used as reference. + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(Location loc) + { + return getHeightNearest(getGeoX(loc.getX()), getGeoY(loc.getY()), loc.getZ()); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param worldX : world x + * @param worldY : world y + * @param worldZ : world z + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(int worldX, int worldY, int worldZ) + { + return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); + } + + /** + * Check line of sight from {@link WorldObject} to {@link WorldObject}.
+ * @param object : The origin object. + * @param target : The target object. + * @return True, when object can see target. + */ + public boolean canSeeTarget(WorldObject object, WorldObject target) + { + // Can always see doors. + if (target.isDoor()) + { + return true; + } + + if (object.getInstanceWorld() != target.getInstanceWorld()) + { + return false; + } + + if (DoorData.getInstance().checkIfDoorsBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld())) + { + return false; + } + + // Get object's and target's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + double theight = 0; + if (target instanceof Creature) + { + theight += (((Creature) target).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + return canSee(object.getX(), object.getY(), object.getZ(), oheight, target.getX(), target.getY(), target.getZ(), theight) && canSee(target.getX(), target.getY(), target.getZ(), theight, object.getX(), object.getY(), object.getZ(), oheight); + } + + /** + * Check line of sight from {@link WorldObject} to {@link Location}.
+ * Note: The check uses {@link Location}'s real Z coordinate (e.g. point above ground), not its geodata representation. + * @param object : The origin object. + * @param position : The target position. + * @return True, when object can see position. + */ + public boolean canSeeLocation(WorldObject object, Location position) + { + // Get object and location coordinates. + int ox = object.getX(); + int oy = object.getY(); + int oz = object.getZ(); + int tx = position.getX(); + int ty = position.getY(); + int tz = position.getZ(); + + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld())) + { + return false; + } + + // Get object's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + // Perform geodata check. + return canSee(ox, oy, oz, oheight, tx, ty, tz, 0) && canSee(tx, ty, tz, 0, ox, oy, oz, oheight); + } + + /** + * Simple check for origin to target visibility.
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param oheight : The height of origin, used as start point. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param theight : The height of target, used as end point. + * @return True, when origin can see target. + */ + public boolean canSee(int ox, int oy, int oz, double oheight, int tx, int ty, int tz, double theight) + { + // Get origin geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get target geodata coordinates. + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check being on same cell and layer (index). + // Note: Get index must use origin height increased by cell height, the method returns index to height exclusive self. + int index = block.getIndexBelow(gox, goy, oz + GeoStructure.CELL_HEIGHT); + if (index < 0) + { + return false; + } + + if ((gox == gtx) && (goy == gty)) + { + return index == block.getIndexBelow(gtx, gty, tz + GeoStructure.CELL_HEIGHT); + } + + // Get ground and nswe flag. + int groundZ = block.getHeight(index); + int nswe = block.getNswe(index); + + // Get delta coordinates, slope of line (XY, XZ) and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double dz = (tz + theight) - (oz + oheight); + final double m = (double) dy / dx; + final double mz = dz / Math.sqrt((dx * dx) + (dy * dy)); + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); + + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) + { + // Set next cell in X direction. + gridX += mdt.getStepX(); + gox += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); - if (!canSeeThrough) - { - return false; - } + // Set next cell in Y direction. + gridY += mdt.getStepY(); + goy += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevGeoZ = curGeoZ; - ++ptIndex; + // Get block of the next cell. + block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get line of sight height (including Z slope). + double losz = oz + oheight + Config.MAX_OBSTACLE_HEIGHT; + losz += mz * Math.sqrt(((checkX - ox) * (checkX - ox)) + ((checkY - oy) * (checkY - oy))); + + // Check line of sight going though wall (vertical check). + + // Get index of particular layer, based on last iterated cell conditions. + boolean canMove = (nswe & dir) != 0; + if (canMove) + { + // No wall present, get next cell below current cell. + index = block.getIndexBelow(gox, goy, groundZ + GeoStructure.CELL_IGNORE_HEIGHT); + } + else + { + // Wall present, get next cell above current cell. + index = block.getIndexAbove(gox, goy, groundZ - (2 * GeoStructure.CELL_HEIGHT)); + } + + // Next cell's does not exist (no geodata with valid condition), return fail. + if (index < 0) + { + return false; + } + + // Get next cell's layer height. + int z = block.getHeight(index); + + // Perform sine of sight check (next cell is above line of sight line), return fail. + if (z > losz) + { + return false; + } + + // Next cell is accessible, update z and NSWE. + groundZ = z; + nswe = block.getNswe(index); } + // Iteration is completed, no obstacle is found. return true; } /** - * Move check. - * @param x the x coordinate - * @param y the y coordinate - * @param zValue the z coordinate - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tzValue the target's z coordinate - * @param instance the instance - * @return the last Location (x,y,z) where player can walk - just before wall + * Check movement from coordinates to coordinates.
+ * Note: The Z coordinates are supposed to be already validated geodata coordinates. + * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return True, when target coordinates are reachable from origin coordinates. */ - public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(x); - final int geoY = getGeoY(y); - final int z = getNearestZ(geoX, geoY, zValue); - final int tGeoX = getGeoX(tx); - final int tGeoY = getGeoY(ty); - final int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return new Location(x, y, getHeight(x, y, z)); - } - if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) - { - return new Location(x, y, getHeight(x, y, z)); + return false; } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = z; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return false; + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + int goz = getHeightNearest(gox, goy, oz); + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check movement within same cell. + if ((gox == gtx) && (goy == gty)) + { + return goz == getHeight(tx, ty, tz); + } + + // Get nswe flag. + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - // Can't move, return previous location. - return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check point heading into obstacle, if so return current point. + if ((nswe & dir) == 0) + { + return false; + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return false; + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != tz)) - { - // Different floors, return start location. - return new Location(x, y, z); - } - - return new Location(tx, ty, tz); + // When origin Z is target Z, the move is successful. + return goz == getHeight(tx, ty, tz); } /** - * Checks if its possible to move from one location to another. - * @param fromX the X coordinate to start checking from - * @param fromY the Y coordinate to start checking from - * @param fromZvalue the Z coordinate to start checking from - * @param toX the X coordinate to end checking at - * @param toY the Y coordinate to end checking at - * @param toZvalue the Z coordinate to end checking at - * @param instance the instance - * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + * Check movement from origin to target coordinates. Returns last available point in the checked path.
+ * Target X and Y reachable and Z is on same floor: + *
    + *
  • Location of the target with corrected Z value from geodata.
  • + *
+ * Target X and Y reachable but Z is on another floor: + *
    + *
  • Location of the origin with corrected Z value from geodata.
  • + *
+ * Target X and Y not reachable: + *
    + *
  • Last accessible location in destination to target.
  • + *
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return The {@link Location} representing last point of movement (e.g. just before wall). */ - public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + public Location getValidLocation(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(fromX); - final int geoY = getGeoY(fromY); - final int fromZ = getNearestZ(geoX, geoY, fromZvalue); - final int tGeoX = getGeoX(toX); - final int tGeoY = getGeoY(toY); - final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); - - if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) - { - return false; + return new Location(ox, oy, oz); } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = fromZ; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return new Location(ox, oy, oz); + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + final int gtz = getHeightNearest(gtx, gty, tz); + int goz = getHeightNearest(gox, goy, oz); + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - return false; - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check target cell is out of geodata grid (world coordinates). + if ((nx < 0) || (nx >= GeoStructure.GEO_CELLS_X) || (ny < 0) || (ny >= GeoStructure.GEO_CELLS_Y)) + { + return new Location(checkX, checkY, goz); + } + + // Check point heading into obstacle, if so return current (border) point. + if ((nswe & dir) == 0) + { + return new Location(checkX, checkY, goz); + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current (border) point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return new Location(checkX, checkY, goz); + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) - { - // Different floors. - return false; - } - - return true; + // Compare Z coordinates: + // If same, path is okay, return target point and fix its Z geodata coordinate. + // If not same, path is does not exist, return origin point. + return goz == gtz ? new Location(tx, ty, gtz) : new Location(ox, oy, oz); } + /** + * Returns the list of location objects as a result of complete path calculation. + * @param ox : origin x + * @param oy : origin y + * @param oz : origin z + * @param tx : target x + * @param ty : target y + * @param tz : target z + * @param instance + * @return {@code List} : complete path from nodes + */ + public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + { + // Get origin and check existing geo coords. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + if (!hasGeoPos(gox, goy)) + { + return Collections.emptyList(); + } + + int goz = getHeightNearest(gox, goy, oz); + + // Get target and check existing geo coords. + int gtx = getGeoX(tx); + int gty = getGeoY(ty); + if (!hasGeoPos(gtx, gty)) + { + return Collections.emptyList(); + } + + int gtz = getHeightNearest(gtx, gty, tz); + + // Prepare buffer for pathfinding calculations. + NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + if (buffer == null) + { + return Collections.emptyList(); + } + + // Find path. + List path = null; + try + { + path = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + if (path.isEmpty()) + { + return Collections.emptyList(); + } + } + catch (Exception e) + { + return Collections.emptyList(); + } + finally + { + buffer.free(); + } + + // Check path. + if (path.size() < 3) + { + return path; + } + + // Get path list iterator. + ListIterator point = path.listIterator(); + + // Get node A (origin). + int nodeAx = ox; + int nodeAy = oy; + int nodeAz = goz; + + // Get node B. + Location nodeB = point.next(); + + // Iterate thought the path to optimize it. + while (point.hasNext()) + { + // Get node C. + Location nodeC = path.get(point.nextIndex()); + + // Check movement from node A to node C. + if (canMoveToTarget(nodeAx, nodeAy, nodeAz, nodeC.getX(), nodeC.getY(), nodeC.getZ(), instance)) + { + // Can move from node A to node C. + // Remove node B. + point.remove(); + } + else + { + // Can not move from node A to node C. + // Set node A (node B is part of path, update A coordinates). + nodeAx = nodeB.getX(); + nodeAy = nodeB.getY(); + nodeAz = nodeB.getZ(); + } + + // Set node B. + nodeB = point.next(); + } + + return path; + } + + /** + * NodeBuffer container with specified size and count of separate buffers. + */ + private static class BufferHolder + { + final int _size; + final List _buffer; + + public BufferHolder(int size, int count) + { + _size = size; + _buffer = new ArrayList<>(count); + + for (int i = 0; i < count; i++) + { + _buffer.add(new NodeBuffer(size)); + } + } + } + + /** + * Returns the instance of the {@link GeoEngine}. + * @return {@link GeoEngine} : The instance. + */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -737,4 +1054,4 @@ public class GeoEngine { protected static final GeoEngine INSTANCE = new GeoEngine(); } -} +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java deleted file mode 100644 index cc76b7523a..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ /dev/null @@ -1,301 +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 . - */ -package org.l2jmobius.gameserver.geoengine; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; -import org.l2jmobius.gameserver.model.World; -import org.l2jmobius.gameserver.model.instancezone.Instance; - -/** - * @author -Nemesiss- - */ -public class GeoEnginePathfinding -{ - private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); - - private BufferInfo[] _buffers; - - protected GeoEnginePathfinding() - { - try - { - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - - _buffers = 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); - } - - _buffers[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"); - } - } - - public boolean pathNodesExist(short regionoffset) - { - return false; - } - - public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) - { - 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)))); - if (buffer == null) - { - return null; - } - - List path = null; - try - { - final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); - - if (result == null) - { - return null; - } - - path = constructPath(result); - } - catch (Exception e) - { - LOGGER.warning(e.getMessage()); - return null; - } - finally - { - buffer.free(); - } - - // check path - if (path.size() < 3) - { - return path; - } - - int currentX, currentY, currentZ; - ListIterator middlePoint; - - middlePoint = path.listIterator(); - currentX = x; - currentY = y; - currentZ = z; - - while (middlePoint.hasNext()) - { - final AbstractNodeLoc locMiddle = middlePoint.next(); - if (!middlePoint.hasNext()) - { - break; - } - - final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); - if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) - { - middlePoint.remove(); - } - else - { - currentX = locMiddle.getX(); - currentY = locMiddle.getY(); - currentZ = locMiddle.getZ(); - } - } - - return path; - } - - /** - * Convert geodata position to pathnode position - * @param geo_pos - * @return pathnode position - */ - public short getNodePos(int geo_pos) - { - return (short) (geo_pos >> 3); // OK? - } - - /** - * Convert node position to pathnode block position - * @param node_pos - * @return pathnode block position (0...255) - */ - public short getNodeBlock(int node_pos) - { - return (short) (node_pos % 256); - } - - public byte getRegionX(int node_pos) - { - return (byte) ((node_pos >> 8) + World.TILE_X_MIN); - } - - public byte getRegionY(int node_pos) - { - return (byte) ((node_pos >> 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 node_x rx - * @return - */ - public int calculateWorldX(short node_x) - { - return World.MAP_MIN_X + (node_x * 128) + 48; - } - - /** - * Convert pathnode y to World y position - * @param node_y - * @return - */ - public int calculateWorldY(short node_y) - { - return World.MAP_MIN_Y + (node_y * 128) + 48; - } - - private List constructPath(AbstractNode nodeValue) - { - final LinkedList path = new LinkedList<>(); - int previousDirectionX = Integer.MIN_VALUE; - int previousDirectionY = Integer.MIN_VALUE; - int directionX, directionY; - - AbstractNode node = nodeValue; - while (node.getParent() != null) - { - directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); - directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); - - // only add a new route point if moving direction changes - if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) - { - previousDirectionX = directionX; - previousDirectionY = directionY; - - path.addFirst(node.getLoc()); - node.setLoc(null); - } - - node = node.getParent(); - } - - return path; - } - - private CellNodeBuffer alloc(int size) - { - CellNodeBuffer current = null; - for (BufferInfo i : _buffers) - { - if (i.mapSize >= size) - { - for (CellNodeBuffer buf : i.buffer) - { - if (buf.lock()) - { - current = buf; - break; - } - } - if (current != null) - { - break; - } - - // not found, allocate temporary buffer - current = new CellNodeBuffer(i.mapSize); - current.lock(); - if (i.buffer.size() < i.count) - { - i.buffer.add(current); - break; - } - } - } - - return current; - } - - private static final class BufferInfo - { - final int mapSize; - final int count; - ArrayList buffer; - - public BufferInfo(int size, int cnt) - { - mapSize = size; - count = cnt; - buffer = new ArrayList<>(count); - } - } - - public static GeoEnginePathfinding getInstance() - { - return SingletonHolder.INSTANCE; - } - - private static class SingletonHolder - { - protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); - } -} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java new file mode 100644 index 0000000000..49ec35aa1c --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java @@ -0,0 +1,85 @@ +/* + * 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 . + */ +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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @param index : Index of the cell. + * @return short : Cell geodata Z coordinate, below given coordinates. + */ + public abstract byte getNswe(int index); +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java new file mode 100644 index 0000000000..f5997bf287 --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java @@ -0,0 +1,130 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java new file mode 100644 index 0000000000..9af9d2a28a --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java @@ -0,0 +1,95 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java new file mode 100644 index 0000000000..31fbf5d477 --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java new file mode 100644 index 0000000000..f77d21d84b --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java @@ -0,0 +1,68 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +public class BlockNull extends ABlock +{ + @Override + public boolean hasGeoPos() + { + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java deleted file mode 100644 index 61ea4fcf82..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java +++ /dev/null @@ -1,48 +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 . - */ -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() - { - } -} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java deleted file mode 100644 index 3c8de1a7f7..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java +++ /dev/null @@ -1,77 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java deleted file mode 100644 index 1ccdefe3da..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java +++ /dev/null @@ -1,56 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java new file mode 100644 index 0000000000..691b164e0c --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ +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_ALL = 0x0F; + + // 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; +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java deleted file mode 100644 index 25eafc5a04..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java +++ /dev/null @@ -1,42 +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 . - */ -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); -} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java deleted file mode 100644 index 0d2c765002..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java +++ /dev/null @@ -1,47 +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 . - */ -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(); -} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java deleted file mode 100644 index 52df457360..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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); - - // 1 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; - } -} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java deleted file mode 100644 index 72a5a635c7..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java +++ /dev/null @@ -1,55 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author HorridoJoho - */ -public final class NullRegion implements IRegion -{ - public static final NullRegion INSTANCE = new NullRegion(); - - @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 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; - } -} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java deleted file mode 100644 index fc924cb875..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java +++ /dev/null @@ -1,92 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java deleted file mode 100644 index 5087513ed9..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java +++ /dev/null @@ -1,87 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public abstract class AbstractNode -{ - private T _loc; - private AbstractNode _parent; - - public AbstractNode(T loc) - { - _loc = loc; - } - - public void setParent(AbstractNode p) - { - _parent = p; - } - - public AbstractNode getParent() - { - return _parent; - } - - public T getLoc() - { - return _loc; - } - - public void setLoc(T l) - { - _loc = l; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); - return result; - } - - @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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java deleted file mode 100644 index 7223b5b2be..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java +++ /dev/null @@ -1,33 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -/** - * @author -Nemesiss- - */ -public abstract class AbstractNodeLoc -{ - public abstract int getX(); - - public abstract int getY(); - - public abstract int getZ(); - - public abstract int getNodeX(); - - public abstract int getNodeY(); -} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java deleted file mode 100644 index 3425234e0e..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public class CellNode extends AbstractNode -{ - 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java deleted file mode 100644 index 522610bb7c..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java +++ /dev/null @@ -1,348 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -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 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) - { - _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(); - } - - public final List debugPath() - { - final List 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 void getNeighbors() - { - if (!_current.getLoc().canGoAll()) - { - 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); - } - - // SouthEast - if ((nodeE != null) && (nodeS != null)) - { - if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) - { - addNode(x + 1, y + 1, z, true); - } - } - - // SouthWest - if ((nodeS != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) - { - addNode(x - 1, y + 1, z, true); - } - } - - // NorthEast - if ((nodeN != null) && (nodeE != null)) - { - if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) - { - addNode(x + 1, y - 1, z, true); - } - } - - // NorthWest - if ((nodeN != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) - { - addNode(x - 1, y - 1, z, true); - } - } - } - - private 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(); - // reinit node if needed - if (result.getLoc() != null) - { - result.getLoc().set(x, y, z); - } - else - { - result.setLoc(new NodeLoc(x, y, z)); - } - } - - return result; - } - - private 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 boolean isHighWeight(int x, int y, int z) - { - final CellNode result = getNode(x, y, z); - if (result == null) - { - return true; - } - - if (!result.getLoc().canGoAll()) - { - return true; - } - if (Math.abs(result.getLoc().getZ() - z) > 16) - { - return true; - } - - return false; - } - - private 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java new file mode 100644 index 0000000000..fa5ca43c2f --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -0,0 +1,111 @@ +/* + * 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 . + */ +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 geodata values. + private int _geoX; + private int _geoY; + private byte _nswe; + + // 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; + + _costG = 0; + _costH = 0; + _costF = 0; + + _parent = null; + } + + public void setGeo(int gx, int gy, int gz, byte nswe) + { + super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); + + _geoX = gx; + _geoY = gy; + _nswe = nswe; + } + + 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 int getCostF() + { + return _costF; + } + + public Node getParent() + { + return _parent; + } + + @Override + public int compareTo(Node o) + { + return _costF - o._costF; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java new file mode 100644 index 0000000000..b464212c96 --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -0,0 +1,368 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.awt.Color; +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; +import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; + +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 _opened; + // Container holding Nodes already explored. + private final List _closed; + + // Target coordinates. + private int _gtx; + private int _gty; + private int _gtz; + + // Pathfinding statistics. + private long _timeStamp; + private long _lastElapsedTime; + + 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 findPath(int gox, int goy, int goz, int gtx, int gty, int gtz) + { + // Set start timestamp. + _timeStamp = System.currentTimeMillis(); + + // 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)); + _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 constructPath() + { + // Create result. + final LinkedList 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; + } + + /** + * Creates list of Nodes to show debug path. + * @param debug : The debug packet to add debug informations in. + */ + public void debugPath(ExServerPrimitive debug) + { + // Add all opened node as yellow points. + for (Node n : _opened) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); + } + + // Add all opened node as blue points. + for (Node n : _closed) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); + } + } + + 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; + + _lastElapsedTime = System.currentTimeMillis() - _timeStamp; + _lock.unlock(); + } + + public long getElapsedTime() + { + return _lastElapsedTime; + } + + /** + * Expand the current {@link Node} by exploring its neighbors (axially and diagonally). + */ + private void expand() + { + // Movement is blocked, skip. + final byte nswe = _current.getNSWE(); + if (nswe == 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; + + // Can move north, expand. + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + } + + // Can move south, expand. + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + } + + // Can move west, expand. + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move east, expand. + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move north-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move north-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + } + + /** + * 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 weight : The weight of movement to the new node. + * @return The nswe of the added node. Blank, if not added. + */ + private byte addNode(int gx, int gy, int gzValue, int weight) + { + // 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]; + + // Set node geodata coordinates. + node.setGeo(gx, gy, gz, nswe); + + // 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, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + _opened.add(node); + _bufferIndex++; + return nswe; + } + + /** + * Calculate cost H value, calculated using diagonal distance method.
+ * 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. + final int dx = Math.abs(gx - _gtx); + final int dy = Math.abs(gy - _gty); + final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + + // Get diagonal and axial differences to the target. + final int dd = Math.min(dx, dy); + final int da = Math.max(dx, dy) - dd; + + // Calculate the diagonal distance of the node to the target. + return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java deleted file mode 100644 index e3bad04b25..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; - -/** - * @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 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 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; - // return super.hashCode(); - } - - @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; - if (_x != other._x) - { - return false; - } - if (_y != other._y) - { - return false; - } - if (_goNorth != other._goNorth) - { - return false; - } - if (_goEast != other._goEast) - { - return false; - } - if (_goSouth != other._goSouth) - { - return false; - } - if (_goWest != other._goWest) - { - return false; - } - if (_geoHeight != other._geoHeight) - { - return false; - } - return true; - } -} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java index 1faf1ae423..ed261765e1 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java @@ -93,15 +93,15 @@ public class ZoneManager implements IXmlReader private static final Map SETTINGS = new HashMap<>(); private static final int SHIFT_BY = 15; - private static final int OFFSET_X = Math.abs(World.MAP_MIN_X >> SHIFT_BY); - private static final int OFFSET_Y = Math.abs(World.MAP_MIN_Y >> SHIFT_BY); + private static final int OFFSET_X = Math.abs(World.WORLD_X_MIN >> SHIFT_BY); + private static final int OFFSET_Y = Math.abs(World.WORLD_Y_MIN >> SHIFT_BY); private final Map, ConcurrentHashMap> _classZones = new ConcurrentHashMap<>(); private final Map _spawnTerritories = new ConcurrentHashMap<>(); private final AtomicInteger _lastDynamicId = new AtomicInteger(300000); private List _debugItems; - private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.MAP_MAX_X >> SHIFT_BY) + OFFSET_X + 1][(World.MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y + 1]; + private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.WORLD_X_MAX >> SHIFT_BY) + OFFSET_X + 1][(World.WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y + 1]; /** * Instantiates a new zone manager. diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/Location.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/Location.java index 73689ab1a6..ca3b22a3f0 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/Location.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/Location.java @@ -16,30 +16,30 @@ */ package org.l2jmobius.gameserver.model; +import java.util.Objects; + +import org.l2jmobius.commons.util.Point2D; import org.l2jmobius.gameserver.model.interfaces.ILocational; import org.l2jmobius.gameserver.model.interfaces.IPositionable; /** - * Location data transfer object.
- * Contains coordinates data, heading and instance Id. - * @author Zoey76 + * A datatype used to retain a 3D (x/y/z/heading) point. It got the capability to be set and cleaned. */ -public class Location implements IPositionable +public class Location extends Point2D implements IPositionable { - protected int _x; - protected int _y; - protected int _z; - private int _heading; + protected volatile int _z; + protected volatile int _heading; public Location(int x, int y, int z) { - this(x, y, z, 0); + super(x, y); + _z = z; + _heading = 0; } public Location(int x, int y, int z, int heading) { - _x = x; - _y = y; + super(x, y); _z = z; _heading = heading; } @@ -51,9 +51,8 @@ public class Location implements IPositionable public Location(StatSet set) { - _x = set.getInt("x"); - _y = set.getInt("y"); - _z = set.getInt("z"); + super(set.getInt("x", 0), set.getInt("y", 0)); + _z = set.getInt("z", 0); _heading = set.getInt("heading", 0); } @@ -146,6 +145,25 @@ public class Location implements IPositionable _heading = loc.getHeading(); } + @Override + public void clean() + { + super.clean(); + _z = 0; + } + + @Override + public Location clone() + { + return new Location(_x, _y, _z); + } + + @Override + public int hashCode() + { + return (31 * super.hashCode()) + Objects.hash(_z); + } + @Override public boolean equals(Object obj) { diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/World.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/World.java index c1510e8c0e..0d796dd834 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/World.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/World.java @@ -66,19 +66,19 @@ public class World public static final int TILE_Y_MAX = 26; public static final int TILE_ZERO_COORD_X = 20; public static final int TILE_ZERO_COORD_Y = 18; - public static final int MAP_MIN_X = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; - public static final int MAP_MIN_Y = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; + public static final int WORLD_X_MIN = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; + public static final int WORLD_Y_MIN = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; - public static final int MAP_MAX_X = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; - public static final int MAP_MAX_Y = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; + public static final int WORLD_X_MAX = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; + public static final int WORLD_Y_MAX = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; /** Calculated offset used so top left region is 0,0 */ - public static final int OFFSET_X = Math.abs(MAP_MIN_X >> SHIFT_BY); - public static final int OFFSET_Y = Math.abs(MAP_MIN_Y >> SHIFT_BY); + public static final int OFFSET_X = Math.abs(WORLD_X_MIN >> SHIFT_BY); + public static final int OFFSET_Y = Math.abs(WORLD_Y_MIN >> SHIFT_BY); /** Number of regions. */ - private static final int REGIONS_X = (MAP_MAX_X >> SHIFT_BY) + OFFSET_X; - private static final int REGIONS_Y = (MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y; + private static final int REGIONS_X = (WORLD_X_MAX >> SHIFT_BY) + OFFSET_X; + private static final int REGIONS_Y = (WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y; /** Map containing all the players in game. */ private static final Map _allPlayers = new ConcurrentHashMap<>(); @@ -817,9 +817,6 @@ public class World return _memberInPartyNumber.get(); } - /** - * @return the current instance of World - */ public static World getInstance() { return SingletonHolder.INSTANCE; diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/WorldObject.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/WorldObject.java index 0e5536bbc0..62313ffea1 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/WorldObject.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/WorldObject.java @@ -188,23 +188,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif synchronized (this) { int spawnX = x; - if (spawnX > World.MAP_MAX_X) + if (spawnX > World.WORLD_X_MAX) { - spawnX = World.MAP_MAX_X - 5000; + spawnX = World.WORLD_X_MAX - 5000; } - if (spawnX < World.MAP_MIN_X) + if (spawnX < World.WORLD_X_MIN) { - spawnX = World.MAP_MIN_X + 5000; + spawnX = World.WORLD_X_MIN + 5000; } int spawnY = y; - if (spawnY > World.MAP_MAX_Y) + if (spawnY > World.WORLD_Y_MAX) { - spawnY = World.MAP_MAX_Y - 5000; + spawnY = World.WORLD_Y_MAX - 5000; } - if (spawnY < World.MAP_MIN_Y) + if (spawnY < World.WORLD_Y_MIN) { - spawnY = World.MAP_MIN_Y + 5000; + spawnY = World.WORLD_Y_MIN + 5000; } // Set the x,y,z position of the WorldObject. If flagged with _isSpawned, setXYZ will automatically update world region, so avoid that. @@ -518,23 +518,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif public void setXYZInvisible(int x, int y, int z) { int correctX = x; - if (correctX > World.MAP_MAX_X) + if (correctX > World.WORLD_X_MAX) { - correctX = World.MAP_MAX_X - 5000; + correctX = World.WORLD_X_MAX - 5000; } - if (correctX < World.MAP_MIN_X) + if (correctX < World.WORLD_X_MIN) { - correctX = World.MAP_MIN_X + 5000; + correctX = World.WORLD_X_MIN + 5000; } int correctY = y; - if (correctY > World.MAP_MAX_Y) + if (correctY > World.WORLD_Y_MAX) { - correctY = World.MAP_MAX_Y - 5000; + correctY = World.WORLD_Y_MAX - 5000; } - if (correctY < World.MAP_MIN_Y) + if (correctY < World.WORLD_Y_MIN) { - correctY = World.MAP_MIN_Y + 5000; + correctY = World.WORLD_Y_MIN + 5000; } setXYZ(correctX, correctY, z); diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/Creature.java index fc29cca657..8593d97e36 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -65,8 +65,6 @@ 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.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -2585,7 +2583,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3406,8 +3404,8 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe final int originalX = x; final int originalY = y; final int originalZ = z; - final int gtx = (originalX - World.MAP_MIN_X) >> 4; - final int gty = (originalY - World.MAP_MIN_Y) >> 4; + final int gtx = (originalX - World.WORLD_X_MIN) >> 4; + final int gty = (originalY - World.WORLD_Y_MIN) >> 4; if (isOnGeodataPath()) { try @@ -3430,7 +3428,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe && !(((curZ - z) > 300) && (distance < 300))) // Prohibit correcting destination if character wants to fall. { // location different if destination wasn't reached (or just z coord is different) - final Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(curX, curY, curZ, x, y, z, getInstanceWorld()); + final Location destiny = GeoEngine.getInstance().getValidLocation(curX, curY, curZ, x, y, z, getInstanceWorld()); x = destiny.getX(); y = destiny.getY(); z = destiny.getZ(); @@ -3444,7 +3442,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { if (isPlayer() && !_isFlying && !isInWater) diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/Summon.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/Summon.java index 2aaa8064dd..4b09eca1d7 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/Summon.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/actor/Summon.java @@ -106,7 +106,7 @@ public abstract class Summon extends Playable final int x = owner.getX(); final int y = owner.getY(); final int z = owner.getZ(); - final Location location = GeoEngine.getInstance().canMoveToTargetLoc(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, owner.getInstanceWorld()); + final Location location = GeoEngine.getInstance().getValidLocation(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, getInstanceWorld()); setXYZInvisible(location.getX(), location.getY(), location.getZ()); } diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java index 334bab47ea..9b0c7059f1 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java @@ -1507,7 +1507,7 @@ public class ItemInstance extends WorldObject if (dropper != null) { final Instance instance = dropper.getInstanceWorld(); - final Location dropDest = GeoEngine.getInstance().canMoveToTargetLoc(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); + final Location dropDest = GeoEngine.getInstance().getValidLocation(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); x = dropDest.getX(); y = dropDest.getY(); z = dropDest.getZ(); diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java index 5d294fe47c..558bf92edf 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java @@ -1179,7 +1179,7 @@ public class SkillCaster implements Runnable } } - final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().canMoveToTargetLoc(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); + final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().getValidLocation(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); creature.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); creature.broadcastPacket(new FlyToLocation(creature, destination, flyType, 0, 0, 333)); diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java index c62c821f44..30068134e3 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java @@ -114,7 +114,7 @@ public class ValidatePosition implements IClientIncomingPacket { if (player.isFalling(_z)) { - final int nearestZ = GeoEngine.getInstance().getHigherHeight(_x, _y, _z); + final int nearestZ = GeoEngine.getInstance().getHeight(_x, _y, _z); if (player.getZ() < nearestZ) { player.setXYZ(_x, _y, nearestZ); diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/util/GeoUtils.java index 4600a0fc53..19a979819e 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -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.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; @@ -30,21 +30,21 @@ public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getInstance().getWorldX(iter.x()); - final int wy = GeoEngine.getInstance().getWorldY(iter.y()); + final int wx = GeoEngine.getWorldX(iter.x()); + final int wy = GeoEngine.getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public final class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); - int wy = GeoEngine.getInstance().getWorldY(prevY); + int wx = GeoEngine.getWorldX(prevX); + int wy = GeoEngine.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.getInstance().getWorldX(curX); - wy = GeoEngine.getInstance().getWorldY(curY); + wx = GeoEngine.getWorldX(curX); + wy = GeoEngine.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().checkNearestNswe(x, y, z, nswe)) + if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0) { return Color.GREEN; } @@ -109,9 +109,8 @@ public final class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final GeoEngine ge = GeoEngine.getInstance(); - final int playerGx = ge.getGeoX(player.getX()); - final int playerGy = ge.getGeoY(player.getY()); + final int playerGx = GeoEngine.getGeoX(player.getX()); + final int playerGy = GeoEngine.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -135,32 +134,32 @@ public final class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = ge.getWorldX(gx); - final int y = ge.getWorldY(gy); - final int z = ge.getNearestZ(gx, gy, player.getZ()); + final int x = GeoEngine.getWorldX(gx); + final int y = GeoEngine.getWorldY(gy); + final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); + Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); 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, Cell.NSWE_EAST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); 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, Cell.NSWE_SOUTH); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); 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, Cell.NSWE_WEST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); 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); @@ -188,41 +187,41 @@ public final class GeoUtils { if (y > lastY) { - return Cell.NSWE_SOUTH_EAST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E; } else if (y < lastY) { - return Cell.NSWE_NORTH_EAST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E; } else { - return Cell.NSWE_EAST; + return GeoStructure.CELL_FLAG_E; } } else if (x < lastX) // west { if (y > lastY) { - return Cell.NSWE_SOUTH_WEST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W; } else if (y < lastY) { - return Cell.NSWE_NORTH_WEST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W; } else { - return Cell.NSWE_WEST; + return GeoStructure.CELL_FLAG_W; } } else // unchanged x { if (y > lastY) { - return Cell.NSWE_SOUTH; + return GeoStructure.CELL_FLAG_S; } else if (y < lastY) { - return Cell.NSWE_NORTH; + return GeoStructure.CELL_FLAG_N; } else { diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/config/GeoEngine.ini b/L2J_Mobius_Classic_2.2_Antharas/dist/game/config/GeoEngine.ini index 609e8a89a7..e740c678dd 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Classic_2.2_Antharas/dist/game/config/GeoEngine.ini @@ -6,33 +6,44 @@ # at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ 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 + # ================================================================= -# Path finding +# Pathfinding # ================================================================= # 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 +# an alternative path (e.g. walk around obstacle), default: True +PathFinding = True -# Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 -PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 +# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 -# Weight for nodes without obstacles far from walls -LowWeight = 0.5 +# Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 +MoveWeight = 10 +MoveWeightDiag = 14 -# Weight for nodes near walls -MediumWeight = 2 +# 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 -# Weight for nodes with obstacles -HighWeight = 3 +# 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. -# Default: LowWeight * sqrt(2) -DiagonalWeight = 0.707 +# Maximum number of generated nodes per one path-finding process, default 3500 +MaxIterations = 3500 # ================================================================= -# Other +# Line of Sight # ================================================================= -# Correct player Z after falling through the ground. -CorrectPlayerZ = False +# 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 diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/geodata/L2D_GeoEngine.patch b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/geodata/L2D_GeoEngine.patch deleted file mode 100644 index f8577e3a0e..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/geodata/L2D_GeoEngine.patch +++ /dev/null @@ -1,6035 +0,0 @@ -Index: dist/game/GeoDataConverter.bat -=================================================================== ---- dist/game/GeoDataConverter.bat (nonexistent) -+++ dist/game/GeoDataConverter.bat (working copy) -@@ -0,0 +1,6 @@ -+@echo off -+title L2D geodata converter -+ -+java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter -+ -+pause -Index: dist/game/GeoDataConverter.sh -=================================================================== ---- dist/game/GeoDataConverter.sh (nonexistent) -+++ dist/game/GeoDataConverter.sh (working copy) -@@ -0,0 +1,4 @@ -+#! /bin/sh -+ -+java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 -+ -Index: dist/game/config/GeoEngine.ini -=================================================================== ---- dist/game/config/GeoEngine.ini (revision 8409) -+++ dist/game/config/GeoEngine.ini (working copy) -@@ -1,6 +1,10 @@ - # ================================================================= - # Geodata - # ================================================================= -+# Because of real-time performance we are using geodata files only in -+# diagonal L2D format now (using filename e.g. 22_16.l2d). -+# L2D geodata can be obtained by conversion of existing L2J or L2OFF geodata. -+# Launch "GeoDataConverter.bat/sh" and follow instructions to start the conversion. - - # 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/ -@@ -13,6 +17,16 @@ - CoordSynchronize = 2 - - # ================================================================= -+# Path checking -+# ================================================================= -+ -+# 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 finding - # ================================================================= - -@@ -23,19 +37,23 @@ - # Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - --# Weight for nodes without obstacles far from walls --LowWeight = 0.5 -+# Base path weight, when moving from one node to another on axis direction, default: 10 -+BaseWeight = 10 - --# Weight for nodes near walls --MediumWeight = 2 -+# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -+DiagonalWeight = 14 - --# Weight for nodes with obstacles --HighWeight = 3 -+# When movement flags of target node is blocked to any direction, multiply movement weight by this multiplier. -+# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 10 -+ObstacleMultiplier = 10 - --# Weight for diagonal movement. --# Default: LowWeight * sqrt(2) --DiagonalWeight = 0.707 -+# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 20 -+# For proper function must be higher than BaseWeight and/or DiagonalWeight. -+HeuristicWeight = 20 - -+# Maximum number of generated nodes per one path-finding process, default 3500 -+MaxIterations = 3500 -+ - # ================================================================= - # Other - # ================================================================= -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (working copy) -@@ -55,9 +55,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -@@ -73,9 +72,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (working copy) -@@ -18,11 +18,11 @@ - - import java.util.List; - --import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -+import org.l2jmobius.gameserver.geoengine.GeoEngine; - import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -+import org.l2jmobius.gameserver.network.SystemMessageId; - import org.l2jmobius.gameserver.util.BuilderUtil; - - public class AdminPathNode implements IAdminCommandHandler -@@ -37,29 +37,30 @@ - { - if (command.equals("admin_path_find")) - { -- if (!Config.PATHFINDING) -- { -- BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); -- return true; -- } - if (activeChar.getTarget() != null) - { -- final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); -+ final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); - if (path == null) - { -- BuilderUtil.sendSysMessage(activeChar, "No Route!"); -- return true; -+ BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); - } -- for (AbstractNodeLoc a : path) -+ else - { -- BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); -+ for (Location point : path) -+ { -+ BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); -+ } - } - } - else - { -- BuilderUtil.sendSysMessage(activeChar, "No Target!"); -+ activeChar.sendPacket(SystemMessageId.INVALID_TARGET); - } - } -+ else -+ { -+ return false; -+ } - return true; - } - -Index: java/org/l2jmobius/Config.java -=================================================================== ---- java/org/l2jmobius/Config.java (revision 8409) -+++ java/org/l2jmobius/Config.java (working copy) -@@ -32,7 +32,6 @@ - import java.net.UnknownHostException; - import java.nio.charset.StandardCharsets; - import java.nio.file.Files; --import java.nio.file.Path; - import java.nio.file.Paths; - import java.time.Duration; - import java.util.ArrayList; -@@ -966,14 +965,17 @@ - // -------------------------------------------------- - // GeoEngine - // -------------------------------------------------- -- public static Path GEODATA_PATH; -+ public static String GEODATA_PATH; -+ public static int COORD_SYNCHRONIZE; -+ public static int PART_OF_CHARACTER_HEIGHT; -+ public static int MAX_OBSTACLE_HEIGHT; - public static boolean PATHFINDING; - public static String PATHFIND_BUFFERS; -- public static float LOW_WEIGHT; -- public static float MEDIUM_WEIGHT; -- public static float HIGH_WEIGHT; -- public static float DIAGONAL_WEIGHT; -- public static int COORD_SYNCHRONIZE; -+ public static int BASE_WEIGHT; -+ public static int DIAGONAL_WEIGHT; -+ public static int HEURISTIC_WEIGHT; -+ public static int OBSTACLE_MULTIPLIER; -+ public static int MAX_ITERATIONS; - public static boolean CORRECT_PLAYER_Z; - - /** Attribute System */ -@@ -2568,14 +2570,17 @@ - - // Load GeoEngine config file (if exists) - final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); -- GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); -+ GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); -+ COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); -+ MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); - PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -- LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); -- MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); -- HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); -- DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); -- COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); -+ DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); -+ OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); -+ HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); -+ MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); - CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); - - // Load AllowedPlayerRaces config file (if exists) -Index: java/org/l2jmobius/gameserver/geoengine/GeoEngine.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (revision 8435) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (working copy) -@@ -16,100 +16,90 @@ - */ - package org.l2jmobius.gameserver.geoengine; - --import java.io.IOException; -+import java.io.File; - import java.io.RandomAccessFile; - import java.nio.ByteOrder; --import java.nio.channels.FileChannel.MapMode; --import java.nio.file.Files; --import java.nio.file.Path; --import java.util.concurrent.atomic.AtomicReferenceArray; --import java.util.logging.Level; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.List; - import java.util.logging.Logger; - - import org.l2jmobius.Config; - import org.l2jmobius.gameserver.data.xml.DoorData; - import org.l2jmobius.gameserver.data.xml.FenceData; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; --import org.l2jmobius.gameserver.geoengine.geodata.IRegion; --import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; --import org.l2jmobius.gameserver.geoengine.geodata.Region; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.World; - import org.l2jmobius.gameserver.model.WorldObject; -+import org.l2jmobius.gameserver.model.actor.Creature; - import org.l2jmobius.gameserver.model.instancezone.Instance; --import org.l2jmobius.gameserver.model.interfaces.ILocational; --import org.l2jmobius.gameserver.util.GeoUtils; --import org.l2jmobius.gameserver.util.LinePointIterator; --import org.l2jmobius.gameserver.util.LinePointIterator3D; -+import org.l2jmobius.gameserver.util.MathUtil; - - /** -- * @author -Nemesiss-, HorridoJoho -+ * @author Hasha - */ - public class GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); -+ protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - -- private static final int WORLD_MIN_X = -655360; -- private static final int WORLD_MIN_Y = -589824; -- private static final int WORLD_MIN_Z = -16384; -+ private final ABlock[][] _blocks; -+ private final BlockNull _nullBlock; - -- /** 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; -- -- /** The regions array */ -- private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); -- -- private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; -- private static final int ELEVATED_SEE_OVER_DISTANCE = 2; -- private static final int MAX_SEE_OVER_HEIGHT = 48; -- -- protected GeoEngine() -+ /** -+ * GeoEngine constructor. Loads all geodata files of chosen geodata format. -+ */ -+ public GeoEngine() - { - LOGGER.info("GeoEngine: Initializing..."); -- for (int i = 0; i < _regions.length(); i++) -- { -- _regions.set(i, NullRegion.INSTANCE); -- } - -+ // initialize block container -+ _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; -+ -+ // load null block -+ _nullBlock = new BlockNull(); -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files according to geoengine config setup - int loaded = 0; -- try -+ long fileSize = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { -- for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { -- for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) -+ final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); -+ if (f.exists() && !f.isDirectory()) - { -- final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); -- if (Files.exists(geoFilePath)) -+ // region file is load-able, try to load it -+ if (loadGeoBlocks(rx, ry)) - { -- try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) -- { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -- loaded++; -- } -+ loaded++; -+ fileSize += f.length(); - } - } -+ else -+ { -+ // region file is not load-able, load null blocks -+ loadNullBlocks(rx, ry); -+ } - } - } -- catch (Exception e) -+ -+ LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -+ if (loaded > 0) - { -- LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); -- System.exit(1); -+ LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); - } - -- LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -- -- // Avoid wrong configs when no files are loaded. -+ // avoid wrong configs when no files are loaded - if (loaded == 0) - { - if (Config.PATHFINDING) -@@ -123,619 +113,820 @@ - LOGGER.info("GeoEngine: Forcing CoordSynchronize setting to -1."); - } - } -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); - } - - /** -- * @param geoX -- * @param geoY -- * @return the region -+ * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. -+ * @return boolean : True, when geodata file was loaded without problem. - */ -- private IRegion getRegion(int geoX, int geoY) -+ private final boolean loadGeoBlocks(int regionX, int regionY) - { -- final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); -- if ((region < 0) || (region >= _regions.length())) -+ final String filename = String.format(GeoFormat.L2D.getFilename(), regionX, regionY); -+ -+ // standard load -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) - { -- return null; -+ // initialize file buffer -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, GeoFormat.L2D); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ } -+ -+ // check data consistency -+ if (buffer.remaining() > 0) -+ { -+ LOGGER.warning("GeoEngine: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ } -+ -+ // loading was successful -+ return true; - } -- return _regions.get(region); -- } -- -- /** -- * @param filePath -- * @param regionX -- * @param regionY -- * @throws IOException -- */ -- 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")) -+ catch (Exception e) - { -- _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -+ // an error occured while loading, load null blocks -+ LOGGER.warning("GeoEngine: Error while loading " + filename + " region file."); -+ LOGGER.warning(e.getMessage()); -+ -+ // replace whole region file with null blocks -+ loadNullBlocks(regionX, regionY); -+ -+ // loading was not successful -+ return false; - } - } - - /** -- * @param regionX -- * @param regionY -+ * Loads null blocks. Used when no region file is detected or an error occurs during loading. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. - */ -- public void unloadRegion(int regionX, int regionY) -+ private final void loadNullBlocks(int regionX, int regionY) - { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); -- } -- -- /** -- * @param geoX -- * @param geoY -- * @return if geodata exist -- */ -- public boolean hasGeoPos(int geoX, int geoY) -- { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // load all null blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { -- return false; -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[blockX + ix][blockY + iy] = _nullBlock; -+ } - } -- return region.hasGeo(); - } - -+ // GEODATA - GENERAL -+ - /** -- * Checks the specified position for available geodata. -- * @param x the world x -- * @param y the world y -- * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise -+ * Converts world X to geodata X. -+ * @param worldX -+ * @return int : Geo X - */ -- public boolean hasGeo(int x, int y) -+ public static int getGeoX(int worldX) - { -- return hasGeoPos(getGeoX(x), getGeoY(y)); -+ return (MathUtil.limit(worldX, World.MAP_MIN_X, World.MAP_MAX_X) - World.MAP_MIN_X) >> 4; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe check -+ * Converts world Y to geodata Y. -+ * @param worldY -+ * @return int : Geo Y - */ -- public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) -+ public static int getGeoY(int worldY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return true; -- } -- return region.checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(worldY, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.MAP_MIN_Y) >> 4; - } - - /** -+ * Converts geodata X to world X. - * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe anti-corner cut -+ * @return int : World X - */ -- public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) -+ public static int getWorldX(int geoX) - { -- boolean can = true; -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- return can && checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(geoX, 0, GeoStructure.GEO_CELLS_X) << 4) + World.MAP_MIN_X + 8; - } - - /** -- * @param geoX -+ * Converts geodata Y to world Y. - * @param geoY -- * @param worldZ -- * @return the nearest Z value -+ * @return int : World Y - */ -- public int getNearestZ(int geoX, int geoY, int worldZ) -+ public static int getWorldY(int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return worldZ; -- } -- return region.getNearestZ(geoX, geoY, worldZ); -+ return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next lower Z value -+ * Returns block of geodata on given coordinates. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return {@link ABlock} : Block of geodata. - */ -- public int getNextLowerZ(int geoX, int geoY, int worldZ) -+ private final ABlock getBlock(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final int x = geoX / GeoStructure.BLOCK_CELLS_X; -+ final int y = geoY / GeoStructure.BLOCK_CELLS_Y; -+ -+ // if x or y is out of array return null -+ if ((x > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) - { -- return worldZ; -+ return _blocks[x][y]; - } -- return region.getNextLowerZ(geoX, geoY, worldZ); -+ return null; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next higher Z value -+ * Check if geo coordinates has geo. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return boolean : True, if given geo coordinates have geodata - */ -- public int getNextHigherZ(int geoX, int geoY, int worldZ) -+ public boolean hasGeoPos(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final ABlock block = getBlock(geoX, geoY); -+ if (block == null) // null block check - { -- return worldZ; -+ // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) -+ // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); -+ return false; - } -- return region.getNextHigherZ(geoX, geoY, worldZ); -+ return block.hasGeoPos(); - } - - /** -- * Gets the Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the height of cell, which is closest to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, closest to given coordinates. - */ -- public int getHeight(int x, int y, int z) -+ public short getHeightNearest(int geoX, int geoY, int worldZ) - { -- return getNearestZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getHeightNearest(geoX, geoY, worldZ) : (short) worldZ; - } - - /** -- * Gets the next lower Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the NSWE flag byte of cell, which is closes to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. - */ -- public int getLowerHeight(int x, int y, int z) -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) - { -- return getNextLowerZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getNsweNearest(geoX, geoY, worldZ) : (byte) 0xFF; - } - - /** -- * Gets the next higher Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Check if world coordinates has geo. -+ * @param worldX : World X -+ * @param worldY : World Y -+ * @return boolean : True, if given world coordinates have geodata - */ -- public int getHigherHeight(int x, int y, int z) -+ public boolean hasGeo(int worldX, int worldY) - { -- return getNextHigherZ(getGeoX(x), getGeoY(y), z); -+ return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); - } - - /** -- * @param worldX -- * @return the geo X -+ * Returns closest Z coordinate according to geodata. -+ * @param worldX : world x -+ * @param worldY : world y -+ * @param worldZ : world z -+ * @return short : nearest Z coordinates according to geodata - */ -- public int getGeoX(int worldX) -+ public short getHeight(int worldX, int worldY, int worldZ) - { -- return (worldX - WORLD_MIN_X) / 16; -+ return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - -- /** -- * @param worldY -- * @return the geo Y -- */ -- public int getGeoY(int worldY) -- { -- return (worldY - WORLD_MIN_Y) / 16; -- } -+ // PATHFINDING - - /** -- * @param worldZ -- * @return the geo Z -+ * Check line of sight from {@link WorldObject} to {@link WorldObject}. -+ * @param origin : The origin object. -+ * @param target : The target object. -+ * @return {@code boolean} : True if origin can see target - */ -- public int getGeoZ(int worldZ) -+ public boolean canSeeTarget(WorldObject origin, WorldObject target) - { -- return (worldZ - WORLD_MIN_Z) / 16; -- } -- -- /** -- * @param geoX -- * @return the world X -- */ -- public int getWorldX(int geoX) -- { -- return (geoX * 16) + WORLD_MIN_X + 8; -- } -- -- /** -- * @param geoY -- * @return the world Y -- */ -- public int getWorldY(int geoY) -- { -- return (geoY * 16) + WORLD_MIN_Y + 8; -- } -- -- /** -- * @param geoZ -- * @return the world Z -- */ -- public int getWorldZ(int geoZ) -- { -- return (geoZ * 16) + WORLD_MIN_Z + 8; -- } -- -- /** -- * Can see target. Doors as target always return true. Checks doors between. -- * @param cha the character -- * @param target the target -- * @return {@code true} if the character can see the target (LOS), {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, WorldObject target) -- { -- if (target.isDoor()) -+ if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) - { -- // Can always see doors. - return true; - } -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param cha the character -- * @param worldPosition the world position -- * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) -- { -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param world -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @param tworld the target's instanceId -- * @return -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) -- { -- return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instance -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) -- { -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) -+ -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = target.getX(); -+ final int ty = target.getY(); -+ final int tz = target.getZ(); -+ -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } -- return canSeeTarget(x, y, z, tx, ty, tz); -- } -- -- /** -- * @param prevX -- * @param prevY -- * @param prevGeoZ -- * @param curX -- * @param curY -- * @param nswe -- * @return the LOS Z value -- */ -- private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) -- { -- if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { -- throw new RuntimeException("Multiple directions!"); -+ return false; - } - -- if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- return getNearestZ(curX, curY, prevGeoZ); -+ return true; - } - -- return getNextHigherZ(curX, curY, prevGeoZ); -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) -+ { -+ return true; -+ } -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) -+ { -+ return goz == gtz; -+ } -+ -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) -+ { -+ oheight = ((Creature) origin).getCollisionHeight() * 2; -+ } -+ -+ double theight = 0; -+ if (target.isCreature()) -+ { -+ theight = ((Creature) target).getCollisionHeight() * 2; -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); - } - - /** -- * Can see target. Does not check doors between. -- * @param xValue the x coordinate -- * @param yValue the y coordinate -- * @param zValue the z coordinate -- * @param txValue the target's x coordinate -- * @param tyValue the target's y coordinate -- * @param tzValue the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -+ * Check line of sight from {@link WorldObject} to {@link Location}. -+ * @param origin : The origin object. -+ * @param position : The target position. -+ * @return {@code boolean} : True if object can see position - */ -- public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) -+ public boolean canSeeTarget(WorldObject origin, Location position) - { -- int x = xValue; -- int y = yValue; -- int tx = txValue; -- int ty = tyValue; -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = position.getX(); -+ final int ty = position.getY(); -+ final int tz = position.getZ(); - -- int geoX = getGeoX(x); -- int geoY = getGeoY(y); -- int tGeoX = getGeoX(tx); -- int tGeoY = getGeoY(ty); -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) -+ { -+ return false; -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) -+ { -+ return false; -+ } - -- int z = getNearestZ(geoX, geoY, zValue); -- int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- // Fastpath. -- if ((geoX == tGeoX) && (geoY == tGeoY)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- if (hasGeoPos(tGeoX, tGeoY)) -- { -- return z == tz; -- } - return true; - } - -- if (tz > z) -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) - { -- int tmp = tx; -- tx = x; -- x = tmp; -- -- tmp = ty; -- ty = y; -- y = tmp; -- -- tmp = tz; -- tz = z; -- z = tmp; -- -- tmp = tGeoX; -- tGeoX = geoX; -- geoX = tmp; -- -- tmp = tGeoY; -- tGeoY = geoY; -- geoY = tmp; -+ return goz == gtz; - } - -- final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); -- // First point is guaranteed to be available, skip it, we can always see our own position. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- final int prevZ = pointIter.z(); -- int prevGeoZ = prevZ; -- int ptIndex = 0; -- while (pointIter.next()) -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight(); -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); -+ } -+ -+ /** -+ * Simple check for origin to target visibility. -+ * @param goxValue : origin X geodata coordinate -+ * @param goyValue : origin Y geodata coordinate -+ * @param gozValue : origin Z geodata coordinate -+ * @param oheight : origin height (if instance of {@link Character}) -+ * @param gtxValue : target X geodata coordinate -+ * @param gtyValue : target Y geodata coordinate -+ * @param gtzValue : target Z geodata coordinate -+ * @param theight : target height (if instance of {@link Character}) -+ * @param instance -+ * @return {@code boolean} : True, when target can be seen. -+ */ -+ private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) -+ { -+ int goz = gozValue; -+ int gtz = gtzValue; -+ int gox = goxValue; -+ int goy = goyValue; -+ int gtx = gtxValue; -+ int gty = gtyValue; -+ -+ // get line of sight Z coordinates -+ double losoz = goz + ((oheight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ double lostz = gtz + ((theight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ -+ // get X delta and signum -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirox = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; -+ final byte dirtx = sx > 0 ? GeoStructure.CELL_FLAG_W : GeoStructure.CELL_FLAG_E; -+ -+ // get Y delta and signum -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte diroy = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ final byte dirty = sy > 0 ? GeoStructure.CELL_FLAG_N : GeoStructure.CELL_FLAG_S; -+ -+ // get Z delta -+ final int dm = Math.max(dx, dy); -+ final double dz = (lostz - losoz) / dm; -+ -+ // get direction flag for diagonal movement -+ final byte diroxy = getDirXY(dirox, diroy); -+ final byte dirtxy = getDirXY(dirtx, dirty); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte diro; -+ byte dirt; -+ -+ // initialize node values -+ int nox = gox; -+ int noy = goy; -+ int ntx = gtx; -+ int nty = gty; -+ byte nsweo = getNsweNearest(gox, goy, goz); -+ byte nswet = getNsweNearest(gtx, gty, gtz); -+ -+ // loop -+ ABlock block; -+ int index; -+ for (int i = 0; i < ((dm + 1) / 2); i++) -+ { -+ // reset direction flag -+ diro = 0; -+ dirt = 0; - -- if ((curX == prevX) && (curY == prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- continue; -+ // calculate next point XY coordinates -+ d -= dy; -+ d += dx; -+ nox += sx; -+ ntx -= sx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroxy; -+ dirt |= dirtxy; - } -+ else if (e2 > -dy) -+ { -+ // calculate next point X coordinate -+ d -= dy; -+ nox += sx; -+ ntx -= sx; -+ diro |= dirox; -+ dirt |= dirtx; -+ } -+ else if (e2 < dx) -+ { -+ // calculate next point Y coordinate -+ d += dx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroy; -+ dirt |= dirty; -+ } - -- final int beeCurZ = pointIter.z(); -- int curGeoZ = prevGeoZ; -- -- // Check if the position has geodata. -- if (hasGeoPos(curX, curY)) - { -- final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); -- curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); -- int maxHeight; -- if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) -+ // get block of the next cell -+ block = getBlock(nox, noy); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nsweo & diro) == 0) - { -- maxHeight = z + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); - } - else - { -- maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); - } - -- boolean canSeeThrough = false; -- if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) -+ // layer does not exist, return -+ if (index == -1) - { -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else -- { -- canSeeThrough = true; -- } -+ return false; - } - -- if (!canSeeThrough) -+ // get layer and next line of sight Z coordinate -+ goz = block.getHeight(index); -+ losoz += dz; -+ -+ // perform line of sight check, return when fails -+ if ((goz - losoz) > Config.MAX_OBSTACLE_HEIGHT) - { - return false; - } -+ -+ // get layer nswe -+ nsweo = block.getNswe(index); - } -+ { -+ // get block of the next cell -+ block = getBlock(ntx, nty); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nswet & dirt) == 0) -+ { -+ index = block.getIndexAbove(ntx, nty, gtz - GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ else -+ { -+ index = block.getIndexBelow(ntx, nty, gtz + GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ -+ // layer does not exist, return -+ if (index == -1) -+ { -+ return false; -+ } -+ -+ // get layer and next line of sight Z coordinate -+ gtz = block.getHeight(index); -+ lostz -= dz; -+ -+ // perform line of sight check, return when fails -+ if ((gtz - lostz) > Config.MAX_OBSTACLE_HEIGHT) -+ { -+ return false; -+ } -+ -+ // get layer nswe -+ nswet = block.getNswe(index); -+ } - -- prevX = curX; -- prevY = curY; -- prevGeoZ = curGeoZ; -- ++ptIndex; -+ // update coords -+ gox = nox; -+ goy = noy; -+ gtx = ntx; -+ gty = nty; - } - -- return true; -+ // when iteration is completed, compare final Z coordinates -+ return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); - } - - /** -- * Move check. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param zValue the z coordinate -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tzValue the target's z coordinate -- * @param instance the instance -- * @return the last Location (x,y,z) where player can walk - just before wall -+ * Check movement from coordinates to coordinates. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {code boolean} : True if target coordinates are reachable from origin coordinates - */ -- public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) -+ public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- final int geoX = getGeoX(x); -- final int geoY = getGeoY(y); -- final int z = getNearestZ(geoX, geoY, zValue); -- final int tGeoX = getGeoX(tx); -- final int tGeoY = getGeoY(ty); -- final int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } -- if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = z; -+ // perform geodata check -+ final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); -+ return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); -+ } -+ -+ /** -+ * Check movement from origin to target. Returns last available point in the checked path. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {@link Location} : Last point where object can walk (just before wall) -+ */ -+ public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) -+ { -+ return new Location(ox, oy, oz); -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) -+ { -+ return new Location(ox, oy, oz); -+ } - -- while (pointIter.next()) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -- -- if (hasGeoPos(prevX, prevY)) -- { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -- { -- // Can't move, return previous location. -- return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); -- } -- } -- -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return new Location(tx, ty, tz); - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != tz)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- // Different floors, return start location. -- return new Location(x, y, z); -+ return new Location(tx, ty, tz); - } - -- return new Location(tx, ty, tz); -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) -+ { -+ return new Location(tx, ty, tz); -+ } -+ -+ // perform geodata check -+ return checkMove(gox, goy, goz, gtx, gty, gtz, instance); - } - - /** -- * Checks if its possible to move from one location to another. -- * @param fromX the X coordinate to start checking from -- * @param fromY the Y coordinate to start checking from -- * @param fromZvalue the Z coordinate to start checking from -- * @param toX the X coordinate to end checking at -- * @param toY the Y coordinate to end checking at -- * @param toZvalue the Z coordinate to end checking at -- * @param instance the instance -- * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise -+ * With this method you can check if a position is visible or can be reached by beeline movement.
-+ * Target X and Y reachable and Z is on same floor: -+ *
    -+ *
  • Location of the target with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y reachable but Z is on another floor: -+ *
    -+ *
  • Location of the origin with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y not reachable: -+ *
    -+ *
  • Last accessible location in destination to target.
  • -+ *
-+ * @param gox : origin X geodata coordinate -+ * @param goy : origin Y geodata coordinate -+ * @param goz : origin Z geodata coordinate -+ * @param gtx : target X geodata coordinate -+ * @param gty : target Y geodata coordinate -+ * @param gtz : target Z geodata coordinate -+ * @param instance -+ * @return {@link GeoLocation} : The last allowed point of movement. - */ -- public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) -+ protected final GeoLocation checkMove(int gox, int goy, int goz, int gtx, int gty, int gtz, Instance instance) - { -- final int geoX = getGeoX(fromX); -- final int geoY = getGeoY(fromY); -- final int fromZ = getNearestZ(geoX, geoY, fromZvalue); -- final int tGeoX = getGeoX(toX); -- final int tGeoY = getGeoY(toY); -- final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); -- -- if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) -+ if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } -- if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) -+ if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = fromZ; -+ // get X delta, signum and direction flag -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirX = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; - -- while (pointIter.next()) -+ // get Y delta, signum and direction flag -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte dirY = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ -+ // get direction flag for diagonal movement -+ final byte dirXY = getDirXY(dirX, dirY); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte direction; -+ -+ // load pointer coordinates -+ int gpx = gox; -+ int gpy = goy; -+ int gpz = goz; -+ -+ // load next pointer -+ int nx = gpx; -+ int ny = gpy; -+ -+ // loop -+ int count = 0; -+ while (count++ < Config.MAX_ITERATIONS) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -+ direction = 0; - -- if (hasGeoPos(prevX, prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -+ d -= dy; -+ d += dx; -+ nx += sx; -+ ny += sy; -+ direction |= dirXY; -+ } -+ else if (e2 > -dy) -+ { -+ d -= dy; -+ nx += sx; -+ direction |= dirX; -+ } -+ else if (e2 < dx) -+ { -+ d += dx; -+ ny += sy; -+ direction |= dirY; -+ } -+ -+ // obstacle found, return -+ if ((getNsweNearest(gpx, gpy, gpz) & direction) == 0) -+ { -+ return new GeoLocation(gpx, gpy, gpz); -+ } -+ -+ // update pointer coordinates -+ gpx = nx; -+ gpy = ny; -+ gpz = getHeightNearest(nx, ny, gpz); -+ -+ // target coordinates reached -+ if ((gpx == gtx) && (gpy == gty)) -+ { -+ if (gpz == gtz) - { -- return false; -+ // path found, Z coordinates are okay, return target point -+ return new GeoLocation(gtx, gty, gtz); - } -+ -+ // path found, Z coordinates are not okay, return last good point -+ return new GeoLocation(gpx, gpy, gpz); - } -+ } -+ -+ return new GeoLocation(gox, goy, goz); -+ } -+ -+ /** -+ * Returns diagonal NSWE flag format of combined two NSWE flags. -+ * @param dirX : X direction NSWE flag -+ * @param dirY : Y direction NSWE flag -+ * @return byte : NSWE flag of combined direction -+ */ -+ private static byte getDirXY(byte dirX, byte dirY) -+ { -+ // check axis directions -+ if (dirY == GeoStructure.CELL_FLAG_N) -+ { -+ if (dirX == GeoStructure.CELL_FLAG_W) -+ { -+ return GeoStructure.CELL_FLAG_NW; -+ } - -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return GeoStructure.CELL_FLAG_NE; - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) -+ if (dirX == GeoStructure.CELL_FLAG_W) - { -- // Different floors. -- return false; -+ return GeoStructure.CELL_FLAG_SW; - } - -- return true; -+ return GeoStructure.CELL_FLAG_SE; - } - -+ /** -+ * Returns the list of location objects as a result of complete path calculation. -+ * @param ox : origin x -+ * @param oy : origin y -+ * @param oz : origin z -+ * @param tx : target x -+ * @param ty : target y -+ * @param tz : target z -+ * @param instance -+ * @return {@code List} : complete path from nodes -+ */ -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ return null; -+ } -+ -+ /** -+ * Returns the instance of the {@link GeoEngine}. -+ * @return {@link GeoEngine} : The instance. -+ */ - public static GeoEngine getInstance() - { - return SingletonHolder.INSTANCE; -@@ -743,6 +934,6 @@ - - private static class SingletonHolder - { -- protected static final GeoEngine INSTANCE = new GeoEngine(); -+ protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); - } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (working copy) -@@ -20,88 +20,84 @@ - import java.util.LinkedList; - import java.util.List; - import java.util.ListIterator; --import java.util.logging.Level; --import java.util.logging.Logger; - - import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; --import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; --import org.l2jmobius.gameserver.model.World; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -+import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.instancezone.Instance; - - /** -- * @author -Nemesiss- -+ * @author Hasha - */ --public class GeoEnginePathfinding -+final class GeoEnginePathfinding extends GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); -+ // pre-allocated buffers -+ private final BufferHolder[] _buffers; - -- private BufferInfo[] _buffers; -- - protected GeoEnginePathfinding() - { -- try -+ super(); -+ -+ final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ _buffers = new BufferHolder[array.length]; -+ int count = 0; -+ for (int i = 0; i < array.length; i++) - { -- final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ final String buf = array[i]; -+ final String[] args = buf.split("x"); - -- _buffers = new BufferInfo[array.length]; -- -- String buf; -- String[] args; -- for (int i = 0; i < array.length; i++) -+ try - { -- buf = array[i]; -- args = buf.split("x"); -- if (args.length != 2) -- { -- throw new Exception("Invalid buffer definition: " + buf); -- } -- -- _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); -+ final int size = Integer.parseInt(args[1]); -+ count += size; -+ _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); - } -+ catch (Exception e) -+ { -+ LOGGER.warning("GeoEnginePathfinding: Can not load buffer setting: " + buf); -+ } - } -- catch (Exception e) -- { -- LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); -- throw new Error("CellPathFinding: load aborted"); -- } -+ -+ LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); - } - -- public boolean pathNodesExist(short regionoffset) -+ @Override -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- return false; -- } -- -- public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) -- { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -- if (!GeoEngine.getInstance().hasGeo(x, y)) -+ // get origin and check existing geo coords -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { - 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)) -+ -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coords -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { - 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)))); -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // Prepare buffer for pathfinding calculations -+ final NodeBuffer buffer = getBuffer(64 + (2 * Math.max(Math.abs(gox - gtx), Math.abs(goy - gty)))); - if (buffer == null) - { - return null; - } - -- List path = null; -+ // find path -+ List path = null; - try - { -- final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); -- -+ final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); - if (result == null) - { - return null; -@@ -125,33 +121,45 @@ - return path; - } - -- int currentX, currentY, currentZ; -- ListIterator middlePoint; -+ // get path list iterator -+ final ListIterator point = path.listIterator(); - -- middlePoint = path.listIterator(); -- currentX = x; -- currentY = y; -- currentZ = z; -+ // get node A (origin) -+ int nodeAx = gox; -+ int nodeAy = goy; -+ short nodeAz = goz; - -- while (middlePoint.hasNext()) -+ // get node B -+ GeoLocation nodeB = (GeoLocation) point.next(); -+ -+ // iterate thought the path to optimize it -+ int count = 0; -+ while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) - { -- final AbstractNodeLoc locMiddle = middlePoint.next(); -- if (!middlePoint.hasNext()) -- { -- break; -- } -+ // get node C -+ final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - -- final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); -- if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) -+ // check movement from node A to node C -+ final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); -+ if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) - { -- middlePoint.remove(); -+ // can move from node A to node C -+ -+ // remove node B -+ point.remove(); - } - else - { -- currentX = locMiddle.getX(); -- currentY = locMiddle.getY(); -- currentZ = locMiddle.getZ(); -+ // can not move from node A to node C -+ -+ // set node A (node B is part of path, update A coordinates) -+ nodeAx = nodeB.getGeoX(); -+ nodeAy = nodeB.getGeoY(); -+ nodeAz = (short) nodeB.getZ(); - } -+ -+ // set node B -+ nodeB = (GeoLocation) point.next(); - } - - return path; -@@ -158,144 +166,102 @@ - } - - /** -- * Convert geodata position to pathnode position -- * @param geo_pos -- * @return pathnode position -+ * Create list of node locations as result of calculated buffer node tree. -+ * @param node : the entry point -+ * @return List : list of node location - */ -- public short getNodePos(int geo_pos) -+ private static List constructPath(Node node) - { -- return (short) (geo_pos >> 3); // OK? -- } -- -- /** -- * Convert node position to pathnode block position -- * @param node_pos -- * @return pathnode block position (0...255) -- */ -- public short getNodeBlock(int node_pos) -- { -- return (short) (node_pos % 256); -- } -- -- public byte getRegionX(int node_pos) -- { -- return (byte) ((node_pos >> 8) + World.TILE_X_MIN); -- } -- -- public byte getRegionY(int node_pos) -- { -- return (byte) ((node_pos >> 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 node_x rx -- * @return -- */ -- public int calculateWorldX(short node_x) -- { -- return World.MAP_MIN_X + (node_x * 128) + 48; -- } -- -- /** -- * Convert pathnode y to World y position -- * @param node_y -- * @return -- */ -- public int calculateWorldY(short node_y) -- { -- return World.MAP_MIN_Y + (node_y * 128) + 48; -- } -- -- private List constructPath(AbstractNode nodeValue) -- { -- final LinkedList path = new LinkedList<>(); -- int previousDirectionX = Integer.MIN_VALUE; -- int previousDirectionY = Integer.MIN_VALUE; -- int directionX, directionY; -+ // create empty list -+ final LinkedList list = new LinkedList<>(); - -- AbstractNode node = nodeValue; -- while (node.getParent() != null) -+ // set direction X/Y -+ int dx = 0; -+ int dy = 0; -+ -+ // get target parent -+ Node target = node; -+ Node parent = target.getParent(); -+ -+ // while parent exists -+ int count = 0; -+ while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { -- directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); -- directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); -+ // get parent <> target direction X/Y -+ final int nx = parent.getLoc().getGeoX() - target.getLoc().getGeoX(); -+ final int ny = parent.getLoc().getGeoY() - target.getLoc().getGeoY(); - -- // only add a new route point if moving direction changes -- if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) -+ // direction has changed? -+ if ((dx != nx) || (dy != ny)) - { -- previousDirectionX = directionX; -- previousDirectionY = directionY; -+ // add node to the beginning of the list -+ list.addFirst(target.getLoc()); - -- path.addFirst(node.getLoc()); -- node.setLoc(null); -+ // update direction X/Y -+ dx = nx; -+ dy = ny; - } - -- node = node.getParent(); -+ // move to next node, set target and get its parent -+ target = parent; -+ parent = target.getParent(); - } - -- return path; -+ // return list -+ return list; - } - -- private CellNodeBuffer alloc(int size) -+ /** -+ * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer. -+ * @param size : pre-calculated minimal required size -+ * @return NodeBuffer : buffer -+ */ -+ private final NodeBuffer getBuffer(int size) - { -- CellNodeBuffer current = null; -- for (BufferInfo i : _buffers) -+ NodeBuffer current = null; -+ for (BufferHolder holder : _buffers) - { -- if (i.mapSize >= size) -+ // Find proper size of buffer -+ if (holder._size < size) - { -- for (CellNodeBuffer buf : i.buffer) -+ continue; -+ } -+ -+ // Find unlocked NodeBuffer -+ for (NodeBuffer buffer : holder._buffer) -+ { -+ if (!buffer.isLocked()) - { -- if (buf.lock()) -- { -- current = buf; -- break; -- } -+ continue; - } -- if (current != null) -- { -- break; -- } - -- // not found, allocate temporary buffer -- current = new CellNodeBuffer(i.mapSize); -- current.lock(); -- if (i.buffer.size() < i.count) -- { -- i.buffer.add(current); -- break; -- } -+ return buffer; - } -+ -+ // NodeBuffer not found, allocate temporary buffer -+ current = new NodeBuffer(holder._size); -+ current.isLocked(); - } - - return current; - } - -- private static final class BufferInfo -+ /** -+ * NodeBuffer container with specified size and count of separate buffers. -+ */ -+ private static final class BufferHolder - { -- final int mapSize; -- final int count; -- ArrayList buffer; -+ final int _size; -+ List _buffer; - -- public BufferInfo(int size, int cnt) -+ public BufferHolder(int size, int count) - { -- mapSize = size; -- count = cnt; -- buffer = new ArrayList<>(count); -+ _size = size; -+ _buffer = new ArrayList<>(count); -+ for (int i = 0; i < count; i++) -+ { -+ _buffer.add(new NodeBuffer(size)); -+ } - } - } -- -- public static GeoEnginePathfinding getInstance() -- { -- return SingletonHolder.INSTANCE; -- } -- -- private static class SingletonHolder -- { -- protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); -- } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (working copy) -@@ -0,0 +1,197 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @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 height of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getHeightNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first above given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, above given coordinates. -+ */ -+ public abstract short getHeightAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first below given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is closest to given coordinates. -+ * @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 the NSWE flag byte of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getNsweNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first above given coordinates. -+ * @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 getNsweAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first below given coordinates. -+ * @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 getNsweBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is closes layer to given coordinates. -+ * @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. -+ * @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 above given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is first layer below given coordinates. -+ * @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 index to data of the cell, which is first layer below given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexBelowOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeight(int index); -+ -+ /** -+ * Returns the height of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightOriginal(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNswe(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNsweOriginal(int index); -+ -+ /** -+ * Sets the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @param nswe : New NSWE flag byte. -+ */ -+ public abstract void setNswe(int index, byte nswe); -+ -+ /** -+ * Saves the block in L2D format to {@link BufferedOutputStream}. Used only for L2D geodata conversion. -+ * @param stream : The stream. -+ * @throws IOException : Can't save the block to steam. -+ */ -+ public abstract void saveBlock(BufferedOutputStream stream) throws IOException; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (working copy) -@@ -0,0 +1,252 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockComplex(ByteBuffer bb, GeoFormat format) -+ { -+ // initialize buffer -+ _buffer = new byte[GeoStructure.BLOCK_CELLS * 3]; -+ -+ // load data -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // 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); -+ } -+ else -+ { -+ // get nswe -+ final byte nswe = bb.get(); -+ _buffer[i * 3] = nswe; -+ -+ // get height -+ final short height = bb.getShort(); -+ _buffer[(i * 3) + 1] = (byte) (height & 0x00FF); -+ _buffer[(i * 3) + 2] = (byte) (height >> 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height > worldZ ? height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height < worldZ ? height : Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public 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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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 int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_COMPLEX_L2D); -+ -+ // write block data -+ stream.write(_buffer, 0, GeoStructure.BLOCK_CELLS * 3); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (working copy) -@@ -0,0 +1,176 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockFlat extends ABlock -+{ -+ protected final short _height; -+ protected byte _nswe; -+ -+ /** -+ * Creates FlatBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockFlat(ByteBuffer bb, GeoFormat format) -+ { -+ _height = bb.getShort(); -+ _nswe = format != GeoFormat.L2D ? 0x0F : (byte) (0xFF); -+ if (format == GeoFormat.L2OFF) -+ { -+ bb.getShort(); -+ } -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return true; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height > worldZ ? _height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height < worldZ ? _height : Short.MAX_VALUE; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height > worldZ ? _nswe : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height < worldZ ? _nswe : 0; -+ } -+ -+ @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return index -+ return _height < worldZ ? 0 : -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _nswe = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_FLAT_L2D); -+ -+ // write height -+ stream.write((byte) (_height & 0x00FF)); -+ stream.write((byte) (_height >> 8)); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (working copy) -@@ -0,0 +1,465 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+import java.nio.ByteOrder; -+import java.util.Arrays; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockMultilayer extends ABlock -+{ -+ private static final int MAX_LAYERS = Byte.MAX_VALUE; -+ -+ private static ByteBuffer _temp; -+ -+ /** -+ * Initializes the temporarily buffer. -+ */ -+ public static void initialize() -+ { -+ // initialize temporarily buffer and sorting mechanism -+ _temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3); -+ _temp.order(ByteOrder.LITTLE_ENDIAN); -+ } -+ -+ /** -+ * Releases temporarily buffer. -+ */ -+ public static void release() -+ { -+ _temp = null; -+ } -+ -+ protected byte[] _buffer; -+ -+ /** -+ * Implicit constructor for children class. -+ */ -+ protected BlockMultilayer() -+ { -+ _buffer = null; -+ } -+ -+ /** -+ * Creates MultilayerBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockMultilayer(ByteBuffer bb, GeoFormat format) -+ { -+ // 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 = format != GeoFormat.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++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // get data -+ final short data = bb.getShort(); -+ -+ // add nswe and height -+ _temp.put((byte) (data & 0x000F)); -+ _temp.putShort((short) ((short) (data & 0xFFF0) >> 1)); -+ } -+ else -+ { -+ // add nswe -+ _temp.put(bb.get()); -+ -+ // add height -+ _temp.putShort(bb.getShort()); -+ } -+ } -+ } -+ -+ // 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 height -+ if (height > worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, return minimum value -+ return Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 height -+ if (height < worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, return maximum value -+ return Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 nswe -+ if (height > worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 nswe -+ if (height < worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @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 first layer data (first from bottom) -+ byte layers = _buffer[index++]; -+ -+ // loop though all cell layers, find closest layer -+ 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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ // get height -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(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]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ // get nswe -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ // set nswe -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_MULTILAYER_L2D); -+ -+ // for each cell -+ int index = 0; -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ // write layers count -+ final byte layers = _buffer[index++]; -+ stream.write(layers); -+ -+ // write cell data -+ stream.write(_buffer, index, layers * 3); -+ -+ // move index to next cell -+ index += layers * 3; -+ } -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (working copy) -@@ -0,0 +1,150 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockNull extends ABlock -+{ -+ private final byte _nswe; -+ -+ public BlockNull() -+ { -+ _nswe = (byte) 0xFF; -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return false; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweBelow(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) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) -+ { -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (nonexistent) -@@ -1,48 +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 . -- */ --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() -- { -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (nonexistent) -@@ -1,77 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (nonexistent) -@@ -1,56 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (working copy) -@@ -0,0 +1,39 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public enum GeoFormat -+{ -+ L2J("%d_%d.l2j"), -+ L2OFF("%d_%d_conv.dat"), -+ L2D("%d_%d.l2d"); -+ -+ private final String _filename; -+ -+ private GeoFormat(String filename) -+ { -+ _filename = filename; -+ } -+ -+ public String getFilename() -+ { -+ return _filename; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (working copy) -@@ -0,0 +1,67 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.geoengine.GeoEngine; -+import org.l2jmobius.gameserver.model.Location; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoLocation extends Location -+{ -+ private byte _nswe; -+ -+ public GeoLocation(int x, int y, int z) -+ { -+ super(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public void set(int x, int y, short z) -+ { -+ super.setXYZ(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public int getGeoX() -+ { -+ return _x; -+ } -+ -+ public int getGeoY() -+ { -+ return _y; -+ } -+ -+ @Override -+ public int getX() -+ { -+ return GeoEngine.getWorldX(_x); -+ } -+ -+ @Override -+ public int getY() -+ { -+ return GeoEngine.getWorldY(_y); -+ } -+ -+ public byte getNSWE() -+ { -+ return _nswe; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (working copy) -@@ -0,0 +1,70 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoStructure -+{ -+ // cells -+ public static final byte CELL_FLAG_E = 1 << 0; -+ public static final byte CELL_FLAG_W = 1 << 1; -+ public static final byte CELL_FLAG_S = 1 << 2; -+ public static final byte CELL_FLAG_N = 1 << 3; -+ public static final byte CELL_FLAG_SE = 1 << 4; -+ public static final byte CELL_FLAG_SW = 1 << 5; -+ public static final byte CELL_FLAG_NE = 1 << 6; -+ public static final byte CELL_FLAG_NW = (byte) (1 << 7); -+ -+ public static final int CELL_HEIGHT = 8; -+ public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6; -+ -+ // blocks -+ public static final byte TYPE_FLAT_L2J_L2OFF = 0; -+ public static final byte TYPE_FLAT_L2D = (byte) 0xD0; -+ public static final byte TYPE_COMPLEX_L2J = 1; -+ public static final byte TYPE_COMPLEX_L2OFF = 0x40; -+ public static final byte TYPE_COMPLEX_L2D = (byte) 0xD1; -+ 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) -+ public static final byte TYPE_MULTILAYER_L2D = (byte) 0xD2; -+ -+ 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; -+ -+ // regions -+ 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; -+ -+ // global geodata -+ private static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); -+ private 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; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (nonexistent) -@@ -1,42 +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 . -- */ --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); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (working copy) -@@ -0,0 +1,53 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public interface IGeoObject -+{ -+ /** -+ * Returns geodata X coordinate of the {@link IGeoObject}. -+ * @return int : Geodata X coordinate. -+ */ -+ int getGeoX(); -+ -+ /** -+ * Returns geodata Y coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Y coordinate. -+ */ -+ int getGeoY(); -+ -+ /** -+ * Returns geodata Z coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Z coordinate. -+ */ -+ int getGeoZ(); -+ -+ /** -+ * Returns height of the {@link IGeoObject}. -+ * @return int : Height. -+ */ -+ int getHeight(); -+ -+ /** -+ * Returns {@link IGeoObject} data. -+ * @return byte[][] : {@link IGeoObject} data. -+ */ -+ byte[][] getObjectGeoData(); -+} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (nonexistent) -@@ -1,47 +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 . -- */ --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(); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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); -- -- // 1 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (nonexistent) -@@ -1,55 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --/** -- * @author HorridoJoho -- */ --public final class NullRegion implements IRegion --{ -- public static final NullRegion INSTANCE = new NullRegion(); -- -- @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 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Region.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (nonexistent) -@@ -1,92 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (nonexistent) -@@ -1,87 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public abstract class AbstractNode --{ -- private T _loc; -- private AbstractNode _parent; -- -- public AbstractNode(T loc) -- { -- _loc = loc; -- } -- -- public void setParent(AbstractNode p) -- { -- _parent = p; -- } -- -- public AbstractNode getParent() -- { -- return _parent; -- } -- -- public T getLoc() -- { -- return _loc; -- } -- -- public void setLoc(T l) -- { -- _loc = l; -- } -- -- @Override -- public int hashCode() -- { -- final int prime = 31; -- int result = 1; -- result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); -- return result; -- } -- -- @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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (nonexistent) -@@ -1,33 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --/** -- * @author -Nemesiss- -- */ --public abstract class AbstractNodeLoc --{ -- public abstract int getX(); -- -- public abstract int getY(); -- -- public abstract int getZ(); -- -- public abstract int getNodeX(); -- -- public abstract int getNodeY(); --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (nonexistent) -@@ -1,67 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public class CellNode extends AbstractNode --{ -- 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (nonexistent) -@@ -1,348 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --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 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) -- { -- _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(); -- } -- -- public final List debugPath() -- { -- final List 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 void getNeighbors() -- { -- if (!_current.getLoc().canGoAll()) -- { -- 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); -- } -- -- // SouthEast -- if ((nodeE != null) && (nodeS != null)) -- { -- if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) -- { -- addNode(x + 1, y + 1, z, true); -- } -- } -- -- // SouthWest -- if ((nodeS != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) -- { -- addNode(x - 1, y + 1, z, true); -- } -- } -- -- // NorthEast -- if ((nodeN != null) && (nodeE != null)) -- { -- if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) -- { -- addNode(x + 1, y - 1, z, true); -- } -- } -- -- // NorthWest -- if ((nodeN != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) -- { -- addNode(x - 1, y - 1, z, true); -- } -- } -- } -- -- private 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(); -- // reinit node if needed -- if (result.getLoc() != null) -- { -- result.getLoc().set(x, y, z); -- } -- else -- { -- result.setLoc(new NodeLoc(x, y, z)); -- } -- } -- -- return result; -- } -- -- private 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 boolean isHighWeight(int x, int y, int z) -- { -- final CellNode result = getNode(x, y, z); -- if (result == null) -- { -- return true; -- } -- -- if (!result.getLoc().canGoAll()) -- { -- return true; -- } -- if (Math.abs(result.getLoc().getZ() - z) > 16) -- { -- return true; -- } -- -- return false; -- } -- -- private 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (working copy) -@@ -0,0 +1,87 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+ -+/** -+ * @author Hasha -+ */ -+public class Node -+{ -+ // node coords and nswe flag -+ private GeoLocation _loc; -+ -+ // node parent (for reverse path construction) -+ private Node _parent; -+ // node child (for moving over nodes during iteration) -+ private Node _child; -+ -+ // node G cost (movement cost = parent movement cost + current movement cost) -+ private double _cost = -1000; -+ -+ public void setLoc(int x, int y, int z) -+ { -+ _loc = new GeoLocation(x, y, z); -+ } -+ -+ public GeoLocation getLoc() -+ { -+ return _loc; -+ } -+ -+ public void setParent(Node parent) -+ { -+ _parent = parent; -+ } -+ -+ public Node getParent() -+ { -+ return _parent; -+ } -+ -+ public void setChild(Node child) -+ { -+ _child = child; -+ } -+ -+ public Node getChild() -+ { -+ return _child; -+ } -+ -+ public void setCost(double cost) -+ { -+ _cost = cost; -+ } -+ -+ public double getCost() -+ { -+ return _cost; -+ } -+ -+ public void free() -+ { -+ // reset node location -+ _loc = null; -+ -+ // reset node parent, child and cost -+ _parent = null; -+ _child = null; -+ _cost = -1000; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (working copy) -@@ -0,0 +1,306 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import java.util.concurrent.locks.ReentrantLock; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+ -+/** -+ * @author DS, Hasha; Credits to Diamond -+ */ -+public class NodeBuffer -+{ -+ private final ReentrantLock _lock = new ReentrantLock(); -+ private final int _size; -+ private final Node[][] _buffer; -+ -+ // center coordinates -+ private int _cx = 0; -+ private int _cy = 0; -+ -+ // target coordinates -+ private int _gtx = 0; -+ private int _gty = 0; -+ private short _gtz = 0; -+ -+ private Node _current = null; -+ -+ /** -+ * Constructor of NodeBuffer. -+ * @param size : one dimension size of buffer -+ */ -+ public NodeBuffer(int size) -+ { -+ // set size -+ _size = size; -+ -+ // initialize buffer -+ _buffer = new Node[size][size]; -+ for (int x = 0; x < size; x++) -+ { -+ for (int y = 0; y < size; y++) -+ { -+ _buffer[x][y] = 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 Node : first node of path -+ */ -+ public Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) -+ { -+ // set coordinates (middle of the line (gox,goy) - (gtx,gty), will be in the center of the buffer) -+ _cx = gox + ((gtx - gox - _size) / 2); -+ _cy = goy + ((gty - goy - _size) / 2); -+ -+ _gtx = gtx; -+ _gty = gty; -+ _gtz = gtz; -+ -+ _current = getNode(gox, goy, goz); -+ _current.setCost(getCostH(gox, goy, goz)); -+ -+ int count = 0; -+ do -+ { -+ // reached target? -+ if ((_current.getLoc().getGeoX() == _gtx) && (_current.getLoc().getGeoY() == _gty) && (Math.abs(_current.getLoc().getZ() - _gtz) < 8)) -+ { -+ return _current; -+ } -+ -+ // expand current node -+ expand(); -+ -+ // move pointer -+ _current = _current.getChild(); -+ } -+ while ((_current != null) && (count++ < Config.MAX_ITERATIONS)); -+ -+ return null; -+ } -+ -+ public boolean isLocked() -+ { -+ return _lock.tryLock(); -+ } -+ -+ public void free() -+ { -+ _current = null; -+ -+ for (Node[] nodes : _buffer) -+ { -+ for (Node node : nodes) -+ { -+ if (node.getLoc() != null) -+ { -+ node.free(); -+ } -+ } -+ } -+ -+ _lock.unlock(); -+ } -+ -+ /** -+ * Check _current Node and add its neighbors to the buffer. -+ */ -+ private final void expand() -+ { -+ // can't move anywhere, don't expand -+ final byte nswe = _current.getLoc().getNSWE(); -+ if (nswe == 0) -+ { -+ return; -+ } -+ -+ // get geo coords of the node to be expanded -+ final int x = _current.getLoc().getGeoX(); -+ final int y = _current.getLoc().getGeoY(); -+ final short z = (short) _current.getLoc().getZ(); -+ -+ // can move north, expand -+ if ((nswe & GeoStructure.CELL_FLAG_N) != 0) -+ { -+ addNode(x, y - 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move south, expand -+ if ((nswe & GeoStructure.CELL_FLAG_S) != 0) -+ { -+ addNode(x, y + 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_W) != 0) -+ { -+ addNode(x - 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_E) != 0) -+ { -+ addNode(x + 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move north-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NW) != 0) -+ { -+ addNode(x - 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move north-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NE) != 0) -+ { -+ addNode(x + 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SW) != 0) -+ { -+ addNode(x - 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SE) != 0) -+ { -+ addNode(x + 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ } -+ -+ /** -+ * Returns node, if it exists in buffer. -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param z : node Z coord -+ * @return Node : node, if exits in buffer -+ */ -+ private final Node getNode(int x, int y, short z) -+ { -+ // check node X out of coordinates -+ final int ix = x - _cx; -+ if ((ix < 0) || (ix >= _size)) -+ { -+ return null; -+ } -+ -+ // check node Y out of coordinates -+ final int iy = y - _cy; -+ if ((iy < 0) || (iy >= _size)) -+ { -+ return null; -+ } -+ -+ // get node -+ final Node result = _buffer[ix][iy]; -+ -+ // check and update -+ if (result.getLoc() == null) -+ { -+ result.setLoc(x, y, z); -+ } -+ -+ // return node -+ return result; -+ } -+ -+ /** -+ * Add node given by coordinates to the buffer. -+ * @param x : geo X coord -+ * @param y : geo Y coord -+ * @param z : geo Z coord -+ * @param weight : weight of movement to new node -+ */ -+ private final void addNode(int x, int y, short z, int weight) -+ { -+ // get node to be expanded -+ final Node node = getNode(x, y, z); -+ if (node == null) -+ { -+ return; -+ } -+ -+ // Z distance between nearby cells is higher than cell size -+ if (node.getLoc().getZ() > (z + (2 * GeoStructure.CELL_HEIGHT))) -+ { -+ return; -+ } -+ -+ // node was already expanded, return -+ if (node.getCost() >= 0) -+ { -+ return; -+ } -+ -+ node.setParent(_current); -+ if (node.getLoc().getNSWE() != (byte) 0xFF) -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + (weight * Config.OBSTACLE_MULTIPLIER)); -+ } -+ else -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + weight); -+ } -+ -+ Node current = _current; -+ int count = 0; -+ while ((current.getChild() != null) && (count < (Config.MAX_ITERATIONS * 4))) -+ { -+ count++; -+ if (current.getChild().getCost() > node.getCost()) -+ { -+ node.setChild(current.getChild()); -+ break; -+ } -+ current = current.getChild(); -+ } -+ -+ if (count >= (Config.MAX_ITERATIONS * 4)) -+ { -+ System.err.println("Pathfinding: too long loop detected, cost:" + node.getCost()); -+ } -+ -+ current.setChild(node); -+ } -+ -+ /** -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param i : node Z coord -+ * @return double : node cost -+ */ -+ private final double getCostH(int x, int y, int i) -+ { -+ final int dX = x - _gtx; -+ final int dY = y - _gty; -+ final int dZ = (i - _gtz) / GeoStructure.CELL_HEIGHT; -+ -+ // return (Math.abs(dX) + Math.abs(dY) + Math.abs(dZ)) * Config.HEURISTIC_WEIGHT; // Manhattan distance -+ return Math.sqrt((dX * dX) + (dY * dY) + (dZ * dZ)) * Config.HEURISTIC_WEIGHT; // Direct distance -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -- --/** -- * @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 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 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; -- // return super.hashCode(); -- } -- -- @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; -- if (_x != other._x) -- { -- return false; -- } -- if (_y != other._y) -- { -- return false; -- } -- if (_goNorth != other._goNorth) -- { -- return false; -- } -- if (_goEast != other._goEast) -- { -- return false; -- } -- if (_goSouth != other._goSouth) -- { -- return false; -- } -- if (_goWest != other._goWest) -- { -- return false; -- } -- if (_geoHeight != other._geoHeight) -- { -- return false; -- } -- return true; -- } --} -Index: java/org/l2jmobius/gameserver/model/actor/Creature.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/Creature.java (revision 8428) -+++ java/org/l2jmobius/gameserver/model/actor/Creature.java (working copy) -@@ -65,8 +65,6 @@ - import org.l2jmobius.gameserver.enums.TeleportWhereType; - import org.l2jmobius.gameserver.enums.UserInfoType; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; - import org.l2jmobius.gameserver.instancemanager.IdManager; - import org.l2jmobius.gameserver.instancemanager.MapRegionManager; - import org.l2jmobius.gameserver.instancemanager.QuestManager; -@@ -2566,7 +2564,7 @@ - - public boolean disregardingGeodata; - public int onGeodataPathIndex; -- public List geoPath; -+ public List geoPath; - public int geoPathAccurateTx; - public int geoPathAccurateTy; - public int geoPathGtx; -@@ -3443,7 +3441,7 @@ - if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) - { - // Path calculation -- overrides previous movement check -- m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); -+ m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); - if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found - { - if (isPlayer() && !_isFlying && !isInWater) -Index: java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (revision 8424) -+++ java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (working copy) -@@ -12746,7 +12746,7 @@ - { - if (Config.CORRECT_PLAYER_Z) - { -- final int nearestZ = GeoEngine.getInstance().getHigherHeight(getX(), getY(), getZ()); -+ final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); - if (getZ() < nearestZ) - { - teleToLocation(new Location(getX(), getY(), nearestZ)); -Index: java/org/l2jmobius/gameserver/util/GeoUtils.java -=================================================================== ---- java/org/l2jmobius/gameserver/util/GeoUtils.java (revision 8409) -+++ java/org/l2jmobius/gameserver/util/GeoUtils.java (working copy) -@@ -19,7 +19,7 @@ - import java.awt.Color; - - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; - import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; - -@@ -26,25 +26,25 @@ - /** - * @author HorridoJoho - */ --public final class GeoUtils -+public class GeoUtils - { - public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); - - final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); - - while (iter.next()) - { -- final int wx = GeoEngine.getInstance().getWorldX(iter.x()); -- final int wy = GeoEngine.getInstance().getWorldY(iter.y()); -+ final int wx = GeoEngine.getWorldX(iter.x()); -+ final int wy = GeoEngine.getWorldY(iter.y()); - - prim.addPoint(Color.RED, wx, wy, z); - } -@@ -53,21 +53,21 @@ - - public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); -- int wy = GeoEngine.getInstance().getWorldY(prevY); -+ int wx = GeoEngine.getWorldX(prevX); -+ int wy = GeoEngine.getWorldY(prevY); - int wz = iter.z(); - prim.addPoint(Color.RED, wx, wy, wz); - -@@ -78,8 +78,8 @@ - - if ((curX != prevX) || (curY != prevY)) - { -- wx = GeoEngine.getInstance().getWorldX(curX); -- wy = GeoEngine.getInstance().getWorldY(curY); -+ wx = GeoEngine.getWorldX(curX); -+ wy = GeoEngine.getWorldY(curY); - wz = iter.z(); - - prim.addPoint(Color.RED, wx, wy, wz); -@@ -93,7 +93,7 @@ - - private static Color getDirectionColor(int x, int y, int z, int nswe) - { -- if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) -+ if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) - { - return Color.GREEN; - } -@@ -109,9 +109,8 @@ - int iPacket = 0; - - ExServerPrimitive exsp = null; -- final GeoEngine ge = GeoEngine.getInstance(); -- final int playerGx = ge.getGeoX(player.getX()); -- final int playerGy = ge.getGeoY(player.getY()); -+ final int playerGx = GeoEngine.getGeoX(player.getX()); -+ final int playerGy = GeoEngine.getGeoY(player.getY()); - for (int dx = -geoRadius; dx <= geoRadius; ++dx) - { - for (int dy = -geoRadius; dy <= geoRadius; ++dy) -@@ -135,12 +134,12 @@ - final int gx = playerGx + dx; - final int gy = playerGy + dy; - -- final int x = ge.getWorldX(gx); -- final int y = ge.getWorldY(gy); -- final int z = ge.getNearestZ(gx, gy, player.getZ()); -+ final int x = GeoEngine.getWorldX(gx); -+ final int y = GeoEngine.getWorldY(gy); -+ final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); - - // north arrow -- Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); -+ Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); - 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); -@@ -147,7 +146,7 @@ - exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z); - - // east arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); - 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); -@@ -154,13 +153,13 @@ - exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z); - - // south arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); - 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, Cell.NSWE_WEST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); - 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); -@@ -188,15 +187,15 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_EAST; -+ return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_EAST; -+ return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; - } - else - { -- return Cell.NSWE_EAST; -+ return GeoStructure.CELL_FLAG_E; // Direction.EAST; - } - } - else if (x < lastX) // west -@@ -203,26 +202,27 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_WEST; -+ return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_WEST; -+ return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; - } - else - { -- return Cell.NSWE_WEST; -+ return GeoStructure.CELL_FLAG_W; // Direction.WEST; - } - } -- else // unchanged x -+ else -+ // unchanged x - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH; -+ return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH; -+ return GeoStructure.CELL_FLAG_N; // Direction.NORTH; - } - else - { -Index: java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java -=================================================================== ---- java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (nonexistent) -+++ java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (working copy) -@@ -0,0 +1,386 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.tools.geodataconverter; -+ -+import java.io.BufferedOutputStream; -+import java.io.File; -+import java.io.FileOutputStream; -+import java.io.RandomAccessFile; -+import java.nio.ByteOrder; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.Scanner; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.commons.util.PropertiesParser; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoDataConverter -+{ -+ private static GeoFormat _format; -+ private static ABlock[][] _blocks; -+ -+ public static void main(String[] args) -+ { -+ // initialize config -+ loadGeoengineConfigs(); -+ -+ // get geodata format -+ String type = ""; -+ try (Scanner scn = new Scanner(System.in)) -+ { -+ while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) -+ { -+ System.out.println("GeoDataConverter: Select source geodata type:"); -+ System.out.println(" J: L2J (e.g. 23_22.l2j)"); -+ System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); -+ System.out.println(" E: Exit"); -+ System.out.print("Choice: "); -+ type = scn.next(); -+ } -+ } -+ if (type.equalsIgnoreCase("E")) -+ { -+ System.exit(0); -+ } -+ -+ _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; -+ -+ // start conversion -+ System.out.println("GeoDataConverter: Converting all " + _format + " files."); -+ -+ // initialize geodata container -+ _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files -+ int converted = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) -+ { -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) -+ { -+ final String input = String.format(_format.getFilename(), rx, ry); -+ final String filepath = Config.GEODATA_PATH; -+ final File f = new File(filepath + input); -+ if (f.exists() && !f.isDirectory()) -+ { -+ // load geodata -+ if (!loadGeoBlocks(input)) -+ { -+ System.out.println("GeoDataConverter: Unable to load " + input + " region file."); -+ continue; -+ } -+ -+ // recalculate nswe -+ if (!recalculateNswe()) -+ { -+ System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); -+ continue; -+ } -+ -+ // save geodata -+ final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); -+ if (!saveGeoBlocks(output)) -+ { -+ System.out.println("GeoDataConverter: Unable to save " + output + " region file."); -+ continue; -+ } -+ -+ converted++; -+ System.out.println("GeoDataConverter: Created " + output + " region file."); -+ } -+ } -+ } -+ System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); -+ } -+ -+ /** -+ * Loads geo blocks from buffer of the region file. -+ * @param filename : The name of the to load. -+ * @return boolean : True when successful. -+ */ -+ private static boolean loadGeoBlocks(String filename) -+ { -+ // region file is load-able, try to load it -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) -+ { -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) -+ if (_format == GeoFormat.L2OFF) -+ { -+ for (int i = 0; i < 18; i++) -+ { -+ buffer.get(); -+ } -+ } -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ if (_format == GeoFormat.L2J) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2J: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2J: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ else -+ { -+ // get block type -+ final short type = buffer.getShort(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ default: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ if (buffer.remaining() > 0) -+ { -+ System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ return false; -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); -+ return false; -+ } -+ } -+ -+ /** -+ * Recalculate diagonal flags for the region file. -+ * @return boolean : True when successful. -+ */ -+ private static boolean recalculateNswe() -+ { -+ try -+ { -+ for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) -+ { -+ for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) -+ { -+ // get block -+ final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // skip flat blocks -+ if (block instanceof BlockFlat) -+ { -+ continue; -+ } -+ -+ // for complex and multilayer blocks go though all layers -+ short height = Short.MAX_VALUE; -+ int index; -+ while ((index = block.getIndexBelow(x, y, height)) != -1) -+ { -+ // get height and nswe -+ height = block.getHeight(index); -+ byte nswe = block.getNswe(index); -+ -+ // update nswe with diagonal flags -+ nswe = updateNsweBelow(x, y, height, nswe); -+ -+ // set nswe of the cell -+ block.setNswe(index, nswe); -+ } -+ } -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ /** -+ * Updates the NSWE flag with diagonal flags. -+ * @param x : Geodata X coordinate. -+ * @param y : Geodata Y coordinate. -+ * @param z : Geodata Z coordinate. -+ * @param nsweValue : NSWE flag to be updated. -+ * @return byte : Updated NSWE flag. -+ */ -+ private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) -+ { -+ byte nswe = nsweValue; -+ -+ // calculate virtual layer height -+ final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); -+ -+ // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) -+ final byte nsweN = getNsweBelow(x, y - 1, height); -+ final byte nsweS = getNsweBelow(x, y + 1, height); -+ final byte nsweW = getNsweBelow(x - 1, y, height); -+ final byte nsweE = getNsweBelow(x + 1, y, height); -+ -+ // north-west -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NW; -+ } -+ -+ // north-east -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NE; -+ } -+ -+ // south-west -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SW; -+ } -+ -+ // south-east -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SE; -+ } -+ -+ return nswe; -+ } -+ -+ private static byte getNsweBelow(int geoX, int geoY, short worldZ) -+ { -+ // out of geo coordinates -+ if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) -+ { -+ return 0; -+ } -+ -+ // out of geo coordinates -+ if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) -+ { -+ return 0; -+ } -+ -+ // get block -+ final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // get index, when valid, return nswe -+ final int index = block.getIndexBelow(geoX, geoY, worldZ); -+ return index == -1 ? 0 : block.getNswe(index); -+ } -+ -+ /** -+ * Save region file to file. -+ * @param filename : The name of file to save. -+ * @return boolean : True when successful. -+ */ -+ private static boolean saveGeoBlocks(String filename) -+ { -+ try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) -+ { -+ // loop over region blocks and save each block -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[ix][iy].saveBlock(bos); -+ } -+ } -+ -+ // flush data to file -+ bos.flush(); -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ private static void loadGeoengineConfigs() -+ { -+ final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); -+ Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); -+ Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); -+ Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); -+ Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); -+ Config.PATHFINDING = geoData.getBoolean("PathFinding", true); -+ Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -+ Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); -+ Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); -+ Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); -+ Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); -+ Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); -+ } -+} -\ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/geodata/Readme.txt b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/geodata/Readme.txt index a480c8c380..2611882e56 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/geodata/Readme.txt +++ b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/geodata/Readme.txt @@ -23,8 +23,8 @@ a - Prerequisites b - Make it work ---------------------------------------------- -To make geodata working: -* unpack your geodata files into "/data/geodata" folder -* open "/config/GeoEngine.ini" with your favorite text editor and then edit following config: - - CoordSynchronize = 2 -* If you do not use any geodata files, the server will automatically change this setting to -1. +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. diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/ai/others/FleeMonsters.java b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/ai/others/FleeMonsters.java index 4562336103..a971742033 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/ai/others/FleeMonsters.java +++ b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/ai/others/FleeMonsters.java @@ -59,7 +59,7 @@ public class FleeMonsters extends AbstractNpcAI final int posX = (int) (npc.getX() + (FLEE_DISTANCE * Math.cos(radians))); final int posY = (int) (npc.getY() + (FLEE_DISTANCE * Math.sin(radians))); final int posZ = npc.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, attacker.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); return super.onAttack(npc, attacker, damage, isSummon); } diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/ai/others/RandomWalkingGuards.java b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/ai/others/RandomWalkingGuards.java index b99becfdc6..780502d5d6 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/ai/others/RandomWalkingGuards.java +++ b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/ai/others/RandomWalkingGuards.java @@ -55,7 +55,7 @@ public class RandomWalkingGuards extends AbstractNpcAI if (!npc.isInCombat()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, Config.MAX_DRIFT_RANGE); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("RANDOM_WALK", getRandom(MIN_WALK_DELAY, MAX_WALK_DELAY), npc, null); } diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index 524652cbc1..18511c6541 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { @@ -133,8 +133,8 @@ public class AdminGeodata implements IAdminCommandHandler } case "admin_geomap": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y + " (" + ((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + "," + ((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + " to " + ((((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + "," + ((((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + ")"); break; } diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java index f3dffa1227..76572f7f25 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java +++ b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java @@ -57,8 +57,8 @@ public class AdminMissingHtmls implements IAdminCommandHandler { case "admin_geomap_missing_htmls": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final int topLeftX = (x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE; final int topLeftY = (y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE; final int bottomRightX = (((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1; diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index fdb2319138..a0a34f1051 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -19,9 +19,9 @@ package handlers.admincommandhandlers; import java.util.List; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; +import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.util.BuilderUtil; @@ -44,13 +44,13 @@ public class AdminPathNode implements IAdminCommandHandler } if (activeChar.getTarget() != null) { - final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { BuilderUtil.sendSysMessage(activeChar, "No Route!"); return true; } - for (AbstractNodeLoc a : path) + for (Location a : path) { BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/Blink.java b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/Blink.java index 22f2c84eae..22697e8e20 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/Blink.java +++ b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/Blink.java @@ -88,7 +88,7 @@ public class Blink extends AbstractEffect final int y = effected.getY() + y1; final int z = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, destination, _flyType, _flySpeed, _flyDelay, _animationSpeed)); diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/Fear.java b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/Fear.java index 95a5afdf98..88c795e04b 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/Fear.java +++ b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/Fear.java @@ -100,7 +100,7 @@ public class Fear extends AbstractEffect final int posY = (int) (effected.getY() + (FEAR_RANGE * Math.sin(radians))); final int posZ = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); } } diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java index fddd94c60a..55c2e78d8a 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java +++ b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java @@ -57,7 +57,7 @@ public class FlyAway extends AbstractEffect final int y = (int) (effector.getY() - (nRadius * (dy / distance))); final int z = effector.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.broadcastPacket(new FlyToLocation(effected, destination, FlyType.THROW_UP)); effected.setXYZ(destination); diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java index 1fbd8239b0..89ad8670dc 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java +++ b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java @@ -133,7 +133,7 @@ public class KnockBack extends AbstractEffect final int x = (int) (effected.getX() + (_distance * Math.cos(radians))); final int y = (int) (effected.getY() + (_distance * Math.sin(radians))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, loc, _type, _speed, _delay, _animationSpeed)); diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/PullBack.java b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/PullBack.java index ed6a37eff6..38fe2fe9d0 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/PullBack.java +++ b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/PullBack.java @@ -73,7 +73,7 @@ public class PullBack extends AbstractEffect } // In retail, you get debuff, but you are not even moved if there is obstacle. You are still disabled from using skills and moving though. - if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effector.getInstanceWorld())) + if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effected.getInstanceWorld())) { effected.broadcastPacket(new FlyToLocation(effected, effector, _type, _speed, _delay, _animationSpeed)); effected.setXYZ(effector.getX(), effector.getY(), GeoEngine.getInstance().getHeight(effector.getX(), effector.getY(), effector.getZ()) + 10); diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java index d6f9664141..813e496c47 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java +++ b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java @@ -87,7 +87,7 @@ public class TeleportToSummon extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = summon.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java index d3c263978c..c9dc34ac22 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java +++ b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java @@ -76,7 +76,7 @@ public class TeleportToTarget extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java index f2148adb54..c7ca6f64cb 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java +++ b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java @@ -65,7 +65,7 @@ public class RollingDice implements IItemHandler final int x = player.getX() + x1; final int y = player.getY() + y1; final int z = player.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); Broadcast.toSelfAndKnownPlayers(player, new Dice(player.getObjectId(), itemId, number, destination.getX(), destination.getY(), destination.getZ())); final SystemMessage sm = new SystemMessage(SystemMessageId.C1_HAS_ROLLED_A_S2); diff --git a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/targethandlers/Ground.java b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/targethandlers/Ground.java index 82ab321c29..d8c73627f5 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/targethandlers/Ground.java +++ b/L2J_Mobius_Classic_2.2_Antharas/dist/game/data/scripts/handlers/targethandlers/Ground.java @@ -52,7 +52,7 @@ public class Ground implements ITargetTypeHandler return null; } - if (!GeoEngine.getInstance().canSeeTarget(creature, worldPosition)) + if (!GeoEngine.getInstance().canSeeLocation(creature, worldPosition)) { if (sendMessage) { diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/Config.java index 356cab0845..b3cc117028 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/Config.java @@ -61,6 +61,7 @@ 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; @@ -893,12 +894,17 @@ public class Config // GeoEngine // -------------------------------------------------- public static Path GEODATA_PATH; + public static GeoType GEODATA_TYPE; public static boolean PATHFINDING; public static String PATHFIND_BUFFERS; - public static float LOW_WEIGHT; - public static float MEDIUM_WEIGHT; - public static float HIGH_WEIGHT; - public static float DIAGONAL_WEIGHT; + public static int MOVE_WEIGHT; + public static int MOVE_WEIGHT_DIAG; + public static int OBSTACLE_WEIGHT; + 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; /** Attribute System */ public static int S_WEAPON_STONE; @@ -2392,12 +2398,17 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); + GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); - MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); - HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); - DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); + MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); + OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); + HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); + MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); + MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); // Load AllowedPlayerRaces config file (if exists) final PropertiesParser AllowedPlayerRaces = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE); diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/commons/util/CommonUtil.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/commons/util/CommonUtil.java index 14ae3a4560..ab7837d827 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/commons/util/CommonUtil.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/commons/util/CommonUtil.java @@ -584,4 +584,15 @@ public class CommonUtil final DecimalFormat formatter = new DecimalFormat(format, new DecimalFormatSymbols(Locale.ENGLISH)); return formatter.format(value); } + + /** + * @param numToTest : The number to test. + * @param min : The minimum limit. + * @param max : The maximum limit. + * @return the number or one of the limit (mininum / maximum). + */ + public static int limit(int numToTest, int min, int max) + { + return (numToTest > max) ? max : ((numToTest < min) ? min : numToTest); + } } diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/commons/util/Point2D.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/commons/util/Point2D.java new file mode 100644 index 0000000000..ebb6272e5e --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/commons/util/Point2D.java @@ -0,0 +1,180 @@ +/* + * 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 . + */ +package org.l2jmobius.commons.util; + +import java.util.Objects; + +/** + * A datatype used to retain a 2D (x/y) point. It got the capability to be set and cleaned. + */ +public class Point2D +{ + protected volatile int _x; + protected volatile int _y; + + public Point2D(int x, int y) + { + _x = x; + _y = y; + } + + @Override + public Point2D clone() + { + return new Point2D(_x, _y); + } + + @Override + public String toString() + { + return _x + ", " + _y; + } + + @Override + public int hashCode() + { + return Objects.hash(_x, _y); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final Point2D other = (Point2D) obj; + return (_x == other._x) && (_y == other._y); + } + + /** + * @param x : The X coord to test. + * @param y : The Y coord to test. + * @return True if all coordinates equals this {@link Point2D} coordinates. + */ + public boolean equals(int x, int y) + { + return (_x == x) && (_y == y); + } + + public int getX() + { + return _x; + } + + public void setX(int x) + { + _x = x; + } + + public int getY() + { + return _y; + } + + public void setY(int y) + { + _y = y; + } + + public void set(int x, int y) + { + _x = x; + _y = y; + } + + /** + * Refresh the current {@link Point2D} using a reference {@link Point2D} and a distance. The new destination is calculated to go in opposite side of the {@link Point2D} reference.
+ *
+ * This method is perfect to calculate fleeing characters position. + * @param referenceLoc : The Point2D used as position. + * @param distance : The distance to be set between current and new position. + */ + public void setFleeing(Point2D referenceLoc, int distance) + { + final double xDiff = referenceLoc.getX() - _x; + final double yDiff = referenceLoc.getY() - _y; + + final double yxRation = Math.abs(xDiff / yDiff); + + final int y = (int) (distance / (yxRation + 1)); + final int x = (int) (y * yxRation); + + _x += (xDiff < 0 ? x : -x); + _y += (yDiff < 0 ? y : -y); + } + + public void clean() + { + _x = 0; + _y = 0; + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @return The distance between this {@Point2D} and some given coordinates. + */ + public double distance2D(int x, int y) + { + final double dx = (double) _x - x; + final double dy = (double) _y - y; + + return Math.sqrt((dx * dx) + (dy * dy)); + } + + /** + * @param point : The {@link Point2D} to test. + * @return The distance between this {@Point2D} and the {@link Point2D} set as parameter. + */ + public double distance2D(Point2D point) + { + return distance2D(point.getX(), point.getY()); + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of some given coordinates. + */ + public boolean isIn2DRadius(int x, int y, int radius) + { + return distance2D(x, y) < radius; + } + + /** + * @param point : The Point2D to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of the {@link Point2D} set as parameter. + */ + public boolean isIn2DRadius(Point2D point, int radius) + { + return distance2D(point) < radius; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/AttackableAI.java index 547963e3e8..dc29d4cf6e 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/AttackableAI.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/ai/AttackableAI.java @@ -578,7 +578,7 @@ public class AttackableAI extends CreatureAI } // Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast) - final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); + final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); moveTo(moveLoc.getX(), moveLoc.getY(), moveLoc.getZ()); } } @@ -801,7 +801,7 @@ public class AttackableAI extends CreatureAI final int newZ = npc.getZ() + 30; // Mobius: Verify destination. Prevents wall collision issues and fixes monsters not avoiding obstacles. - moveTo(GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); + moveTo(GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); } return; } diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/data/SpawnTable.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/data/SpawnTable.java index a81201f69e..19f0db5b3e 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/data/SpawnTable.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/data/SpawnTable.java @@ -114,8 +114,8 @@ public class SpawnTable } // XML file for spawn - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); // Write info to XML @@ -192,8 +192,8 @@ public class SpawnTable if (update) { - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = spawn.getNpcSpawnTemplate() != null ? spawn.getNpcSpawnTemplate().getSpawnTemplate().getFile() : new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); final File tempFile = new File(spawnFile.getAbsolutePath().substring(Config.DATAPACK_ROOT.getAbsolutePath().length() + 1).replace('\\', '/') + ".tmp"); try diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/enums/GeoType.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/enums/GeoType.java new file mode 100644 index 0000000000..f77aa5eac4 --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/enums/GeoType.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.enums; + +public enum GeoType +{ + L2J("%d_%d.l2j"), + L2OFF("%d_%d_conv.dat"); + + private final String _filename; + + private GeoType(String filename) + { + _filename = filename; + } + + public String getFilename() + { + return _filename; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java new file mode 100644 index 0000000000..e62f50a8df --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java @@ -0,0 +1,144 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 9d255a5f54..7bef9c770b 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,72 +16,63 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.channels.FileChannel.MapMode; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.logging.Level; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; import java.util.logging.Logger; import org.l2jmobius.Config; +import org.l2jmobius.commons.util.CommonUtil; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; -import org.l2jmobius.gameserver.geoengine.geodata.IRegion; -import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; -import org.l2jmobius.gameserver.geoengine.geodata.Region; +import org.l2jmobius.gameserver.enums.GeoType; +import org.l2jmobius.gameserver.enums.MoveDirectionType; +import org.l2jmobius.gameserver.geoengine.geodata.ABlock; +import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; +import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; +import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; +import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.model.interfaces.ILocational; -import org.l2jmobius.gameserver.util.GeoUtils; -import org.l2jmobius.gameserver.util.LinePointIterator; -import org.l2jmobius.gameserver.util.LinePointIterator3D; -/** - * @author -Nemesiss-, HorridoJoho - */ public class GeoEngine { - private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - - private static final int WORLD_MIN_X = -655360; - private static final int WORLD_MIN_Y = -589824; - private static final int WORLD_MIN_Z = -16384; - - /** 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; - - /** The regions array */ - private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; - private static final int ELEVATED_SEE_OVER_DISTANCE = 2; - private static final int MAX_SEE_OVER_HEIGHT = 48; - protected GeoEngine() + private final ABlock[][] _blocks; + private final BlockNull _nullBlock; + + // Pre-allocated buffers. + private final BufferHolder[] _buffers; + + public GeoEngine() { LOGGER.info("GeoEngine: Initializing..."); - for (int i = 0; i < _regions.length(); i++) - { - _regions.set(i, NullRegion.INSTANCE); - } + // Initialize block container. + _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; + + // Load null block. + _nullBlock = new BlockNull(); + + // Initialize multilayer temporarily buffer. + BlockMultilayer.initialize(); + + // Load geo files according to geoengine config setup. int loaded = 0; try { @@ -92,23 +83,52 @@ public class GeoEngine final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); if (Files.exists(geoFilePath)) { - try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + // Region file is load-able, try to load it. + if (loadGeoBlocks(regionX, regionY)) { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); loaded++; } } + else + { + // Region file is not load-able, load null blocks. + loadNullBlocks(regionX, regionY); + } } } } catch (Exception e) { - LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + LOGGER.warning("GeoEngine: Failed to load geodata! " + e); System.exit(1); } - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + // Release multilayer block temporarily buffer. + BlockMultilayer.release(); + + String[] array = Config.PATHFIND_BUFFERS.split(";"); + _buffers = new BufferHolder[array.length]; + + int count = 0; + for (int i = 0; i < array.length; i++) + { + String buf = array[i]; + String[] args = buf.split("x"); + + try + { + int size = Integer.parseInt(args[1]); + count += size; + _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); + } + catch (Exception e) + { + LOGGER.warning("Could not load buffer setting:" + buf + ". " + e); + } + } + LOGGER.info("Loaded " + count + " node buffers."); + // Avoid wrong configs when no files are loaded. if ((loaded == 0) && Config.PATHFINDING) { @@ -118,616 +138,913 @@ public class GeoEngine } /** - * @param geoX - * @param geoY - * @return the region + * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer and log this situation. + * @param size : pre-calculated minimal required size + * @return NodeBuffer : buffer */ - private IRegion getRegion(int geoX, int geoY) + private NodeBuffer getBuffer(int size) { - final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); - if ((region < 0) || (region >= _regions.length())) + NodeBuffer current = null; + for (BufferHolder holder : _buffers) { - return null; - } - return _regions.get(region); - } - - /** - * @param filePath - * @param regionX - * @param regionY - * @throws IOException - */ - 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()).load().order(ByteOrder.LITTLE_ENDIAN))); - } - } - - /** - * @param regionX - * @param regionY - */ - public void unloadRegion(int regionX, int regionY) - { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); - } - - /** - * @param geoX - * @param geoY - * @return if geodata exist - */ - public boolean hasGeoPos(int geoX, int geoY) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return false; - } - return region.hasGeo(); - } - - /** - * Checks the specified position for available geodata. - * @param x the world x - * @param y the world y - * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise - */ - public boolean hasGeo(int x, int y) - { - return hasGeoPos(getGeoX(x), getGeoY(y)); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe check - */ - public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return true; - } - return region.checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe anti-corner cut - */ - public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) - { - boolean can = true; - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - return can && checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the nearest Z value - */ - public int getNearestZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNearestZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next lower Z value - */ - public int getNextLowerZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextLowerZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next higher Z value - */ - public int getNextHigherZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextHigherZ(geoX, geoY, worldZ); - } - - /** - * Gets the Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHeight(int x, int y, int z) - { - return getNearestZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next lower Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getLowerHeight(int x, int y, int z) - { - return getNextLowerZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next higher Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHigherHeight(int x, int y, int z) - { - return getNextHigherZ(getGeoX(x), getGeoY(y), z); - } - - /** - * @param worldX - * @return the geo X - */ - public int getGeoX(int worldX) - { - return (worldX - WORLD_MIN_X) / 16; - } - - /** - * @param worldY - * @return the geo Y - */ - public int getGeoY(int worldY) - { - return (worldY - WORLD_MIN_Y) / 16; - } - - /** - * @param worldZ - * @return the geo Z - */ - public int getGeoZ(int worldZ) - { - return (worldZ - WORLD_MIN_Z) / 16; - } - - /** - * @param geoX - * @return the world X - */ - public int getWorldX(int geoX) - { - return (geoX * 16) + WORLD_MIN_X + 8; - } - - /** - * @param geoY - * @return the world Y - */ - public int getWorldY(int geoY) - { - return (geoY * 16) + WORLD_MIN_Y + 8; - } - - /** - * @param geoZ - * @return the world Z - */ - public int getWorldZ(int geoZ) - { - return (geoZ * 16) + WORLD_MIN_Z + 8; - } - - /** - * Can see target. Doors as target always return true. Checks doors between. - * @param cha the character - * @param target the target - * @return {@code true} if the character can see the target (LOS), {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, WorldObject target) - { - if (target.isDoor()) - { - // Can always see doors. - return true; - } - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); - } - - /** - * Can see target. Checks doors between. - * @param cha the character - * @param worldPosition the world position - * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) - { - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param world - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @param tworld the target's instanceId - * @return - */ - public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) - { - return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instance - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) - { - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) - { - return false; - } - return canSeeTarget(x, y, z, tx, ty, tz); - } - - /** - * @param prevX - * @param prevY - * @param prevGeoZ - * @param curX - * @param curY - * @param nswe - * @return the LOS Z value - */ - private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) - { - if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) - { - throw new RuntimeException("Multiple directions!"); - } - - if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) - { - return getNearestZ(curX, curY, prevGeoZ); - } - - return getNextHigherZ(curX, curY, prevGeoZ); - } - - /** - * Can see target. Does not check doors between. - * @param xValue the x coordinate - * @param yValue the y coordinate - * @param zValue the z coordinate - * @param txValue the target's x coordinate - * @param tyValue the target's y coordinate - * @param tzValue the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) - { - int x = xValue; - int y = yValue; - int tx = txValue; - int ty = tyValue; - - int geoX = getGeoX(x); - int geoY = getGeoY(y); - int tGeoX = getGeoX(tx); - int tGeoY = getGeoY(ty); - - int z = getNearestZ(geoX, geoY, zValue); - int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - // Fastpath. - if ((geoX == tGeoX) && (geoY == tGeoY)) - { - if (hasGeoPos(tGeoX, tGeoY)) - { - return z == tz; - } - return true; - } - - if (tz > z) - { - int tmp = tx; - tx = x; - x = tmp; - - tmp = ty; - ty = y; - y = tmp; - - tmp = tz; - tz = z; - z = tmp; - - tmp = tGeoX; - tGeoX = geoX; - geoX = tmp; - - tmp = tGeoY; - tGeoY = geoY; - geoY = tmp; - } - - final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); - // First point is guaranteed to be available, skip it, we can always see our own position. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - final int prevZ = pointIter.z(); - int prevGeoZ = prevZ; - int ptIndex = 0; - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - - if ((curX == prevX) && (curY == prevY)) + // Find proper size of buffer. + if (holder._size < size) { continue; } - final int beeCurZ = pointIter.z(); - int curGeoZ = prevGeoZ; - - // Check if the position has geodata. - if (hasGeoPos(curX, curY)) + // Find unlocked NodeBuffer. + for (NodeBuffer buffer : holder._buffer) { - final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); - curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); - int maxHeight; - if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) + if (!buffer.isLocked()) { - maxHeight = z + MAX_SEE_OVER_HEIGHT; - } - else - { - maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; + continue; } - boolean canSeeThrough = false; - if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) + return buffer; + } + + LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); + + // NodeBuffer not found, allocate temporary buffer. + current = new NodeBuffer(holder._size); + current.isLocked(); + } + + return current; + } + + /** + * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + * @return boolean : True, when geodata file was loaded without problem. + */ + private boolean loadGeoBlocks(int regionX, int regionY) + { + final String filename = String.format(Config.GEODATA_TYPE.getFilename(), regionX, regionY); + final String filepath = Config.GEODATA_PATH + "\\" + filename; + + // Standard load. + try (RandomAccessFile raf = new RandomAccessFile(filepath, "r"); + FileChannel fc = raf.getChannel()) + { + // Initialize file buffer. + MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // Load 18B header for L2OFF geodata (1st and 2nd byte...region X and Y). + if (Config.GEODATA_TYPE == GeoType.L2OFF) + { + for (int i = 0; i < 18; i++) { - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) + buffer.get(); + } + } + + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Loop over region blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + if (Config.GEODATA_TYPE == GeoType.L2J) { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) - { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); + // Get block type. + final byte type = buffer.get(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + case GeoStructure.TYPE_MULTILAYER_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + default: + { + throw new IllegalArgumentException("Unknown block type: " + type); + } + } } else { - canSeeThrough = true; + // Get block type. + final short type = buffer.getShort(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + default: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + } } } + } + + // Check data consistency. + if (buffer.remaining() > 0) + { + LOGGER.warning("Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); + } + + // Loading was successful. + return true; + } + catch (Exception e) + { + // An error occured while loading, load null blocks. + LOGGER.warning("Error loading " + filename + " region file. " + e); + + // Replace whole region file with null blocks. + loadNullBlocks(regionX, regionY); + + // Loading was not successful. + return false; + } + } + + /** + * Loads null blocks. Used when no region file is detected or an error occurs during loading. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + */ + private void loadNullBlocks(int regionX, int regionY) + { + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Load all null blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + _blocks[blockX + ix][blockY + iy] = _nullBlock; + } + } + } + + /** + * Converts world X to geodata X. + * @param worldX + * @return int : Geo X + */ + public static int getGeoX(int worldX) + { + return (worldX - World.WORLD_X_MIN) >> 4; + } + + /** + * Converts world Y to geodata Y. + * @param worldY + * @return int : Geo Y + */ + public static int getGeoY(int worldY) + { + return (worldY - World.WORLD_Y_MIN) >> 4; + } + + /** + * Converts geodata X to world X. + * @param geoX + * @return int : World X + */ + public static int getWorldX(int geoX) + { + return (geoX << 4) + World.WORLD_X_MIN + 8; + } + + /** + * Converts geodata Y to world Y. + * @param geoY + * @return int : World Y + */ + public static int getWorldY(int geoY) + { + return (geoY << 4) + World.WORLD_Y_MIN + 8; + } + + /** + * Returns block of geodata on given coordinates. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return {@link ABlock} : Block of geodata. + */ + public ABlock getBlock(int geoX, int geoY) + { + final int x = geoX / GeoStructure.BLOCK_CELLS_X; + if ((x < 0) || (x >= GeoStructure.GEO_BLOCKS_X)) + { + return null; + } + final int y = geoY / GeoStructure.BLOCK_CELLS_Y; + if ((y < 0) || (y >= GeoStructure.GEO_BLOCKS_Y)) + { + return null; + } + return _blocks[x][y]; + } + + /** + * Check if geo coordinates has geo. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return boolean : True, if given geo coordinates have geodata + */ + public boolean hasGeoPos(int geoX, int geoY) + { + final ABlock block = getBlock(geoX, geoY); + return (block != null) && block.hasGeoPos(); + } + + /** + * Returns the height of cell, which is closest to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell geodata Z coordinate, closest to given coordinates. + */ + public short getHeightNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return (short) worldZ; + } + return block.getHeightNearest(geoX, geoY, worldZ); + } + + /** + * Returns the NSWE flag byte of cell, which is closes to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. + */ + public byte getNsweNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return GeoStructure.CELL_FLAG_ALL; + } + return block.getNsweNearest(geoX, geoY, worldZ); + } + + /** + * Check if world coordinates has geo. + * @param worldX : World X + * @param worldY : World Y + * @return boolean : True, if given world coordinates have geodata + */ + public boolean hasGeo(int worldX, int worldY) + { + return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param loc : The location used as reference. + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(Location loc) + { + return getHeightNearest(getGeoX(loc.getX()), getGeoY(loc.getY()), loc.getZ()); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param worldX : world x + * @param worldY : world y + * @param worldZ : world z + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(int worldX, int worldY, int worldZ) + { + return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); + } + + /** + * Check line of sight from {@link WorldObject} to {@link WorldObject}.
+ * @param object : The origin object. + * @param target : The target object. + * @return True, when object can see target. + */ + public boolean canSeeTarget(WorldObject object, WorldObject target) + { + // Can always see doors. + if (target.isDoor()) + { + return true; + } + + if (object.getInstanceWorld() != target.getInstanceWorld()) + { + return false; + } + + if (DoorData.getInstance().checkIfDoorsBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld())) + { + return false; + } + + // Get object's and target's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + double theight = 0; + if (target instanceof Creature) + { + theight += (((Creature) target).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + return canSee(object.getX(), object.getY(), object.getZ(), oheight, target.getX(), target.getY(), target.getZ(), theight) && canSee(target.getX(), target.getY(), target.getZ(), theight, object.getX(), object.getY(), object.getZ(), oheight); + } + + /** + * Check line of sight from {@link WorldObject} to {@link Location}.
+ * Note: The check uses {@link Location}'s real Z coordinate (e.g. point above ground), not its geodata representation. + * @param object : The origin object. + * @param position : The target position. + * @return True, when object can see position. + */ + public boolean canSeeLocation(WorldObject object, Location position) + { + // Get object and location coordinates. + int ox = object.getX(); + int oy = object.getY(); + int oz = object.getZ(); + int tx = position.getX(); + int ty = position.getY(); + int tz = position.getZ(); + + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld())) + { + return false; + } + + // Get object's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + // Perform geodata check. + return canSee(ox, oy, oz, oheight, tx, ty, tz, 0) && canSee(tx, ty, tz, 0, ox, oy, oz, oheight); + } + + /** + * Simple check for origin to target visibility.
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param oheight : The height of origin, used as start point. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param theight : The height of target, used as end point. + * @return True, when origin can see target. + */ + public boolean canSee(int ox, int oy, int oz, double oheight, int tx, int ty, int tz, double theight) + { + // Get origin geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get target geodata coordinates. + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check being on same cell and layer (index). + // Note: Get index must use origin height increased by cell height, the method returns index to height exclusive self. + int index = block.getIndexBelow(gox, goy, oz + GeoStructure.CELL_HEIGHT); + if (index < 0) + { + return false; + } + + if ((gox == gtx) && (goy == gty)) + { + return index == block.getIndexBelow(gtx, gty, tz + GeoStructure.CELL_HEIGHT); + } + + // Get ground and nswe flag. + int groundZ = block.getHeight(index); + int nswe = block.getNswe(index); + + // Get delta coordinates, slope of line (XY, XZ) and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double dz = (tz + theight) - (oz + oheight); + final double m = (double) dy / dx; + final double mz = dz / Math.sqrt((dx * dx) + (dy * dy)); + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); + + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) + { + // Set next cell in X direction. + gridX += mdt.getStepX(); + gox += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); - if (!canSeeThrough) - { - return false; - } + // Set next cell in Y direction. + gridY += mdt.getStepY(); + goy += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevGeoZ = curGeoZ; - ++ptIndex; + // Get block of the next cell. + block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get line of sight height (including Z slope). + double losz = oz + oheight + Config.MAX_OBSTACLE_HEIGHT; + losz += mz * Math.sqrt(((checkX - ox) * (checkX - ox)) + ((checkY - oy) * (checkY - oy))); + + // Check line of sight going though wall (vertical check). + + // Get index of particular layer, based on last iterated cell conditions. + boolean canMove = (nswe & dir) != 0; + if (canMove) + { + // No wall present, get next cell below current cell. + index = block.getIndexBelow(gox, goy, groundZ + GeoStructure.CELL_IGNORE_HEIGHT); + } + else + { + // Wall present, get next cell above current cell. + index = block.getIndexAbove(gox, goy, groundZ - (2 * GeoStructure.CELL_HEIGHT)); + } + + // Next cell's does not exist (no geodata with valid condition), return fail. + if (index < 0) + { + return false; + } + + // Get next cell's layer height. + int z = block.getHeight(index); + + // Perform sine of sight check (next cell is above line of sight line), return fail. + if (z > losz) + { + return false; + } + + // Next cell is accessible, update z and NSWE. + groundZ = z; + nswe = block.getNswe(index); } + // Iteration is completed, no obstacle is found. return true; } /** - * Move check. - * @param x the x coordinate - * @param y the y coordinate - * @param zValue the z coordinate - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tzValue the target's z coordinate - * @param instance the instance - * @return the last Location (x,y,z) where player can walk - just before wall + * Check movement from coordinates to coordinates.
+ * Note: The Z coordinates are supposed to be already validated geodata coordinates. + * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return True, when target coordinates are reachable from origin coordinates. */ - public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(x); - final int geoY = getGeoY(y); - final int z = getNearestZ(geoX, geoY, zValue); - final int tGeoX = getGeoX(tx); - final int tGeoY = getGeoY(ty); - final int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return new Location(x, y, getHeight(x, y, z)); - } - if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) - { - return new Location(x, y, getHeight(x, y, z)); + return false; } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = z; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return false; + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + int goz = getHeightNearest(gox, goy, oz); + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check movement within same cell. + if ((gox == gtx) && (goy == gty)) + { + return goz == getHeight(tx, ty, tz); + } + + // Get nswe flag. + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - // Can't move, return previous location. - return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check point heading into obstacle, if so return current point. + if ((nswe & dir) == 0) + { + return false; + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return false; + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != tz)) - { - // Different floors, return start location. - return new Location(x, y, z); - } - - return new Location(tx, ty, tz); + // When origin Z is target Z, the move is successful. + return goz == getHeight(tx, ty, tz); } /** - * Checks if its possible to move from one location to another. - * @param fromX the X coordinate to start checking from - * @param fromY the Y coordinate to start checking from - * @param fromZvalue the Z coordinate to start checking from - * @param toX the X coordinate to end checking at - * @param toY the Y coordinate to end checking at - * @param toZvalue the Z coordinate to end checking at - * @param instance the instance - * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + * Check movement from origin to target coordinates. Returns last available point in the checked path.
+ * Target X and Y reachable and Z is on same floor: + *
    + *
  • Location of the target with corrected Z value from geodata.
  • + *
+ * Target X and Y reachable but Z is on another floor: + *
    + *
  • Location of the origin with corrected Z value from geodata.
  • + *
+ * Target X and Y not reachable: + *
    + *
  • Last accessible location in destination to target.
  • + *
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return The {@link Location} representing last point of movement (e.g. just before wall). */ - public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + public Location getValidLocation(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(fromX); - final int geoY = getGeoY(fromY); - final int fromZ = getNearestZ(geoX, geoY, fromZvalue); - final int tGeoX = getGeoX(toX); - final int tGeoY = getGeoY(toY); - final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); - - if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) - { - return false; + return new Location(ox, oy, oz); } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = fromZ; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return new Location(ox, oy, oz); + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + final int gtz = getHeightNearest(gtx, gty, tz); + int goz = getHeightNearest(gox, goy, oz); + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - return false; - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check target cell is out of geodata grid (world coordinates). + if ((nx < 0) || (nx >= GeoStructure.GEO_CELLS_X) || (ny < 0) || (ny >= GeoStructure.GEO_CELLS_Y)) + { + return new Location(checkX, checkY, goz); + } + + // Check point heading into obstacle, if so return current (border) point. + if ((nswe & dir) == 0) + { + return new Location(checkX, checkY, goz); + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current (border) point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return new Location(checkX, checkY, goz); + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) - { - // Different floors. - return false; - } - - return true; + // Compare Z coordinates: + // If same, path is okay, return target point and fix its Z geodata coordinate. + // If not same, path is does not exist, return origin point. + return goz == gtz ? new Location(tx, ty, gtz) : new Location(ox, oy, oz); } + /** + * Returns the list of location objects as a result of complete path calculation. + * @param ox : origin x + * @param oy : origin y + * @param oz : origin z + * @param tx : target x + * @param ty : target y + * @param tz : target z + * @param instance + * @return {@code List} : complete path from nodes + */ + public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + { + // Get origin and check existing geo coords. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + if (!hasGeoPos(gox, goy)) + { + return Collections.emptyList(); + } + + int goz = getHeightNearest(gox, goy, oz); + + // Get target and check existing geo coords. + int gtx = getGeoX(tx); + int gty = getGeoY(ty); + if (!hasGeoPos(gtx, gty)) + { + return Collections.emptyList(); + } + + int gtz = getHeightNearest(gtx, gty, tz); + + // Prepare buffer for pathfinding calculations. + NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + if (buffer == null) + { + return Collections.emptyList(); + } + + // Find path. + List path = null; + try + { + path = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + if (path.isEmpty()) + { + return Collections.emptyList(); + } + } + catch (Exception e) + { + return Collections.emptyList(); + } + finally + { + buffer.free(); + } + + // Check path. + if (path.size() < 3) + { + return path; + } + + // Get path list iterator. + ListIterator point = path.listIterator(); + + // Get node A (origin). + int nodeAx = ox; + int nodeAy = oy; + int nodeAz = goz; + + // Get node B. + Location nodeB = point.next(); + + // Iterate thought the path to optimize it. + while (point.hasNext()) + { + // Get node C. + Location nodeC = path.get(point.nextIndex()); + + // Check movement from node A to node C. + if (canMoveToTarget(nodeAx, nodeAy, nodeAz, nodeC.getX(), nodeC.getY(), nodeC.getZ(), instance)) + { + // Can move from node A to node C. + // Remove node B. + point.remove(); + } + else + { + // Can not move from node A to node C. + // Set node A (node B is part of path, update A coordinates). + nodeAx = nodeB.getX(); + nodeAy = nodeB.getY(); + nodeAz = nodeB.getZ(); + } + + // Set node B. + nodeB = point.next(); + } + + return path; + } + + /** + * NodeBuffer container with specified size and count of separate buffers. + */ + private static class BufferHolder + { + final int _size; + final List _buffer; + + public BufferHolder(int size, int count) + { + _size = size; + _buffer = new ArrayList<>(count); + + for (int i = 0; i < count; i++) + { + _buffer.add(new NodeBuffer(size)); + } + } + } + + /** + * Returns the instance of the {@link GeoEngine}. + * @return {@link GeoEngine} : The instance. + */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -737,4 +1054,4 @@ public class GeoEngine { protected static final GeoEngine INSTANCE = new GeoEngine(); } -} +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java deleted file mode 100644 index cc76b7523a..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ /dev/null @@ -1,301 +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 . - */ -package org.l2jmobius.gameserver.geoengine; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; -import org.l2jmobius.gameserver.model.World; -import org.l2jmobius.gameserver.model.instancezone.Instance; - -/** - * @author -Nemesiss- - */ -public class GeoEnginePathfinding -{ - private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); - - private BufferInfo[] _buffers; - - protected GeoEnginePathfinding() - { - try - { - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - - _buffers = 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); - } - - _buffers[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"); - } - } - - public boolean pathNodesExist(short regionoffset) - { - return false; - } - - public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) - { - 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)))); - if (buffer == null) - { - return null; - } - - List path = null; - try - { - final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); - - if (result == null) - { - return null; - } - - path = constructPath(result); - } - catch (Exception e) - { - LOGGER.warning(e.getMessage()); - return null; - } - finally - { - buffer.free(); - } - - // check path - if (path.size() < 3) - { - return path; - } - - int currentX, currentY, currentZ; - ListIterator middlePoint; - - middlePoint = path.listIterator(); - currentX = x; - currentY = y; - currentZ = z; - - while (middlePoint.hasNext()) - { - final AbstractNodeLoc locMiddle = middlePoint.next(); - if (!middlePoint.hasNext()) - { - break; - } - - final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); - if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) - { - middlePoint.remove(); - } - else - { - currentX = locMiddle.getX(); - currentY = locMiddle.getY(); - currentZ = locMiddle.getZ(); - } - } - - return path; - } - - /** - * Convert geodata position to pathnode position - * @param geo_pos - * @return pathnode position - */ - public short getNodePos(int geo_pos) - { - return (short) (geo_pos >> 3); // OK? - } - - /** - * Convert node position to pathnode block position - * @param node_pos - * @return pathnode block position (0...255) - */ - public short getNodeBlock(int node_pos) - { - return (short) (node_pos % 256); - } - - public byte getRegionX(int node_pos) - { - return (byte) ((node_pos >> 8) + World.TILE_X_MIN); - } - - public byte getRegionY(int node_pos) - { - return (byte) ((node_pos >> 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 node_x rx - * @return - */ - public int calculateWorldX(short node_x) - { - return World.MAP_MIN_X + (node_x * 128) + 48; - } - - /** - * Convert pathnode y to World y position - * @param node_y - * @return - */ - public int calculateWorldY(short node_y) - { - return World.MAP_MIN_Y + (node_y * 128) + 48; - } - - private List constructPath(AbstractNode nodeValue) - { - final LinkedList path = new LinkedList<>(); - int previousDirectionX = Integer.MIN_VALUE; - int previousDirectionY = Integer.MIN_VALUE; - int directionX, directionY; - - AbstractNode node = nodeValue; - while (node.getParent() != null) - { - directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); - directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); - - // only add a new route point if moving direction changes - if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) - { - previousDirectionX = directionX; - previousDirectionY = directionY; - - path.addFirst(node.getLoc()); - node.setLoc(null); - } - - node = node.getParent(); - } - - return path; - } - - private CellNodeBuffer alloc(int size) - { - CellNodeBuffer current = null; - for (BufferInfo i : _buffers) - { - if (i.mapSize >= size) - { - for (CellNodeBuffer buf : i.buffer) - { - if (buf.lock()) - { - current = buf; - break; - } - } - if (current != null) - { - break; - } - - // not found, allocate temporary buffer - current = new CellNodeBuffer(i.mapSize); - current.lock(); - if (i.buffer.size() < i.count) - { - i.buffer.add(current); - break; - } - } - } - - return current; - } - - private static final class BufferInfo - { - final int mapSize; - final int count; - ArrayList buffer; - - public BufferInfo(int size, int cnt) - { - mapSize = size; - count = cnt; - buffer = new ArrayList<>(count); - } - } - - public static GeoEnginePathfinding getInstance() - { - return SingletonHolder.INSTANCE; - } - - private static class SingletonHolder - { - protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); - } -} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java new file mode 100644 index 0000000000..49ec35aa1c --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java @@ -0,0 +1,85 @@ +/* + * 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 . + */ +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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @param index : Index of the cell. + * @return short : Cell geodata Z coordinate, below given coordinates. + */ + public abstract byte getNswe(int index); +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java new file mode 100644 index 0000000000..f5997bf287 --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java @@ -0,0 +1,130 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java new file mode 100644 index 0000000000..9af9d2a28a --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java @@ -0,0 +1,95 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java new file mode 100644 index 0000000000..31fbf5d477 --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java new file mode 100644 index 0000000000..f77d21d84b --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java @@ -0,0 +1,68 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +public class BlockNull extends ABlock +{ + @Override + public boolean hasGeoPos() + { + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java deleted file mode 100644 index 61ea4fcf82..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java +++ /dev/null @@ -1,48 +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 . - */ -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() - { - } -} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java deleted file mode 100644 index 3c8de1a7f7..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java +++ /dev/null @@ -1,77 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java deleted file mode 100644 index 1ccdefe3da..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java +++ /dev/null @@ -1,56 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java new file mode 100644 index 0000000000..691b164e0c --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ +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_ALL = 0x0F; + + // 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; +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java deleted file mode 100644 index 25eafc5a04..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java +++ /dev/null @@ -1,42 +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 . - */ -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); -} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java deleted file mode 100644 index 0d2c765002..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java +++ /dev/null @@ -1,47 +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 . - */ -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(); -} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java deleted file mode 100644 index 52df457360..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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); - - // 1 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; - } -} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java deleted file mode 100644 index 72a5a635c7..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java +++ /dev/null @@ -1,55 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author HorridoJoho - */ -public final class NullRegion implements IRegion -{ - public static final NullRegion INSTANCE = new NullRegion(); - - @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 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; - } -} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java deleted file mode 100644 index fc924cb875..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java +++ /dev/null @@ -1,92 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java deleted file mode 100644 index 5087513ed9..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java +++ /dev/null @@ -1,87 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public abstract class AbstractNode -{ - private T _loc; - private AbstractNode _parent; - - public AbstractNode(T loc) - { - _loc = loc; - } - - public void setParent(AbstractNode p) - { - _parent = p; - } - - public AbstractNode getParent() - { - return _parent; - } - - public T getLoc() - { - return _loc; - } - - public void setLoc(T l) - { - _loc = l; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); - return result; - } - - @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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java deleted file mode 100644 index 7223b5b2be..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java +++ /dev/null @@ -1,33 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -/** - * @author -Nemesiss- - */ -public abstract class AbstractNodeLoc -{ - public abstract int getX(); - - public abstract int getY(); - - public abstract int getZ(); - - public abstract int getNodeX(); - - public abstract int getNodeY(); -} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java deleted file mode 100644 index 3425234e0e..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public class CellNode extends AbstractNode -{ - 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java deleted file mode 100644 index 522610bb7c..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java +++ /dev/null @@ -1,348 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -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 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) - { - _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(); - } - - public final List debugPath() - { - final List 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 void getNeighbors() - { - if (!_current.getLoc().canGoAll()) - { - 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); - } - - // SouthEast - if ((nodeE != null) && (nodeS != null)) - { - if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) - { - addNode(x + 1, y + 1, z, true); - } - } - - // SouthWest - if ((nodeS != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) - { - addNode(x - 1, y + 1, z, true); - } - } - - // NorthEast - if ((nodeN != null) && (nodeE != null)) - { - if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) - { - addNode(x + 1, y - 1, z, true); - } - } - - // NorthWest - if ((nodeN != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) - { - addNode(x - 1, y - 1, z, true); - } - } - } - - private 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(); - // reinit node if needed - if (result.getLoc() != null) - { - result.getLoc().set(x, y, z); - } - else - { - result.setLoc(new NodeLoc(x, y, z)); - } - } - - return result; - } - - private 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 boolean isHighWeight(int x, int y, int z) - { - final CellNode result = getNode(x, y, z); - if (result == null) - { - return true; - } - - if (!result.getLoc().canGoAll()) - { - return true; - } - if (Math.abs(result.getLoc().getZ() - z) > 16) - { - return true; - } - - return false; - } - - private 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java new file mode 100644 index 0000000000..fa5ca43c2f --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -0,0 +1,111 @@ +/* + * 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 . + */ +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 geodata values. + private int _geoX; + private int _geoY; + private byte _nswe; + + // 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; + + _costG = 0; + _costH = 0; + _costF = 0; + + _parent = null; + } + + public void setGeo(int gx, int gy, int gz, byte nswe) + { + super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); + + _geoX = gx; + _geoY = gy; + _nswe = nswe; + } + + 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 int getCostF() + { + return _costF; + } + + public Node getParent() + { + return _parent; + } + + @Override + public int compareTo(Node o) + { + return _costF - o._costF; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java new file mode 100644 index 0000000000..b464212c96 --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -0,0 +1,368 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.awt.Color; +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; +import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; + +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 _opened; + // Container holding Nodes already explored. + private final List _closed; + + // Target coordinates. + private int _gtx; + private int _gty; + private int _gtz; + + // Pathfinding statistics. + private long _timeStamp; + private long _lastElapsedTime; + + 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 findPath(int gox, int goy, int goz, int gtx, int gty, int gtz) + { + // Set start timestamp. + _timeStamp = System.currentTimeMillis(); + + // 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)); + _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 constructPath() + { + // Create result. + final LinkedList 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; + } + + /** + * Creates list of Nodes to show debug path. + * @param debug : The debug packet to add debug informations in. + */ + public void debugPath(ExServerPrimitive debug) + { + // Add all opened node as yellow points. + for (Node n : _opened) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); + } + + // Add all opened node as blue points. + for (Node n : _closed) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); + } + } + + 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; + + _lastElapsedTime = System.currentTimeMillis() - _timeStamp; + _lock.unlock(); + } + + public long getElapsedTime() + { + return _lastElapsedTime; + } + + /** + * Expand the current {@link Node} by exploring its neighbors (axially and diagonally). + */ + private void expand() + { + // Movement is blocked, skip. + final byte nswe = _current.getNSWE(); + if (nswe == 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; + + // Can move north, expand. + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + } + + // Can move south, expand. + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + } + + // Can move west, expand. + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move east, expand. + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move north-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move north-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + } + + /** + * 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 weight : The weight of movement to the new node. + * @return The nswe of the added node. Blank, if not added. + */ + private byte addNode(int gx, int gy, int gzValue, int weight) + { + // 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]; + + // Set node geodata coordinates. + node.setGeo(gx, gy, gz, nswe); + + // 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, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + _opened.add(node); + _bufferIndex++; + return nswe; + } + + /** + * Calculate cost H value, calculated using diagonal distance method.
+ * 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. + final int dx = Math.abs(gx - _gtx); + final int dy = Math.abs(gy - _gty); + final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + + // Get diagonal and axial differences to the target. + final int dd = Math.min(dx, dy); + final int da = Math.max(dx, dy) - dd; + + // Calculate the diagonal distance of the node to the target. + return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java deleted file mode 100644 index e3bad04b25..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; - -/** - * @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 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 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; - // return super.hashCode(); - } - - @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; - if (_x != other._x) - { - return false; - } - if (_y != other._y) - { - return false; - } - if (_goNorth != other._goNorth) - { - return false; - } - if (_goEast != other._goEast) - { - return false; - } - if (_goSouth != other._goSouth) - { - return false; - } - if (_goWest != other._goWest) - { - return false; - } - if (_geoHeight != other._geoHeight) - { - return false; - } - return true; - } -} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java index 1faf1ae423..ed261765e1 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java @@ -93,15 +93,15 @@ public class ZoneManager implements IXmlReader private static final Map SETTINGS = new HashMap<>(); private static final int SHIFT_BY = 15; - private static final int OFFSET_X = Math.abs(World.MAP_MIN_X >> SHIFT_BY); - private static final int OFFSET_Y = Math.abs(World.MAP_MIN_Y >> SHIFT_BY); + private static final int OFFSET_X = Math.abs(World.WORLD_X_MIN >> SHIFT_BY); + private static final int OFFSET_Y = Math.abs(World.WORLD_Y_MIN >> SHIFT_BY); private final Map, ConcurrentHashMap> _classZones = new ConcurrentHashMap<>(); private final Map _spawnTerritories = new ConcurrentHashMap<>(); private final AtomicInteger _lastDynamicId = new AtomicInteger(300000); private List _debugItems; - private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.MAP_MAX_X >> SHIFT_BY) + OFFSET_X + 1][(World.MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y + 1]; + private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.WORLD_X_MAX >> SHIFT_BY) + OFFSET_X + 1][(World.WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y + 1]; /** * Instantiates a new zone manager. diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/Location.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/Location.java index 73689ab1a6..ca3b22a3f0 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/Location.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/Location.java @@ -16,30 +16,30 @@ */ package org.l2jmobius.gameserver.model; +import java.util.Objects; + +import org.l2jmobius.commons.util.Point2D; import org.l2jmobius.gameserver.model.interfaces.ILocational; import org.l2jmobius.gameserver.model.interfaces.IPositionable; /** - * Location data transfer object.
- * Contains coordinates data, heading and instance Id. - * @author Zoey76 + * A datatype used to retain a 3D (x/y/z/heading) point. It got the capability to be set and cleaned. */ -public class Location implements IPositionable +public class Location extends Point2D implements IPositionable { - protected int _x; - protected int _y; - protected int _z; - private int _heading; + protected volatile int _z; + protected volatile int _heading; public Location(int x, int y, int z) { - this(x, y, z, 0); + super(x, y); + _z = z; + _heading = 0; } public Location(int x, int y, int z, int heading) { - _x = x; - _y = y; + super(x, y); _z = z; _heading = heading; } @@ -51,9 +51,8 @@ public class Location implements IPositionable public Location(StatSet set) { - _x = set.getInt("x"); - _y = set.getInt("y"); - _z = set.getInt("z"); + super(set.getInt("x", 0), set.getInt("y", 0)); + _z = set.getInt("z", 0); _heading = set.getInt("heading", 0); } @@ -146,6 +145,25 @@ public class Location implements IPositionable _heading = loc.getHeading(); } + @Override + public void clean() + { + super.clean(); + _z = 0; + } + + @Override + public Location clone() + { + return new Location(_x, _y, _z); + } + + @Override + public int hashCode() + { + return (31 * super.hashCode()) + Objects.hash(_z); + } + @Override public boolean equals(Object obj) { diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/World.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/World.java index c1510e8c0e..0d796dd834 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/World.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/World.java @@ -66,19 +66,19 @@ public class World public static final int TILE_Y_MAX = 26; public static final int TILE_ZERO_COORD_X = 20; public static final int TILE_ZERO_COORD_Y = 18; - public static final int MAP_MIN_X = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; - public static final int MAP_MIN_Y = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; + public static final int WORLD_X_MIN = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; + public static final int WORLD_Y_MIN = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; - public static final int MAP_MAX_X = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; - public static final int MAP_MAX_Y = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; + public static final int WORLD_X_MAX = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; + public static final int WORLD_Y_MAX = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; /** Calculated offset used so top left region is 0,0 */ - public static final int OFFSET_X = Math.abs(MAP_MIN_X >> SHIFT_BY); - public static final int OFFSET_Y = Math.abs(MAP_MIN_Y >> SHIFT_BY); + public static final int OFFSET_X = Math.abs(WORLD_X_MIN >> SHIFT_BY); + public static final int OFFSET_Y = Math.abs(WORLD_Y_MIN >> SHIFT_BY); /** Number of regions. */ - private static final int REGIONS_X = (MAP_MAX_X >> SHIFT_BY) + OFFSET_X; - private static final int REGIONS_Y = (MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y; + private static final int REGIONS_X = (WORLD_X_MAX >> SHIFT_BY) + OFFSET_X; + private static final int REGIONS_Y = (WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y; /** Map containing all the players in game. */ private static final Map _allPlayers = new ConcurrentHashMap<>(); @@ -817,9 +817,6 @@ public class World return _memberInPartyNumber.get(); } - /** - * @return the current instance of World - */ public static World getInstance() { return SingletonHolder.INSTANCE; diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/WorldObject.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/WorldObject.java index 0e5536bbc0..62313ffea1 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/WorldObject.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/WorldObject.java @@ -188,23 +188,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif synchronized (this) { int spawnX = x; - if (spawnX > World.MAP_MAX_X) + if (spawnX > World.WORLD_X_MAX) { - spawnX = World.MAP_MAX_X - 5000; + spawnX = World.WORLD_X_MAX - 5000; } - if (spawnX < World.MAP_MIN_X) + if (spawnX < World.WORLD_X_MIN) { - spawnX = World.MAP_MIN_X + 5000; + spawnX = World.WORLD_X_MIN + 5000; } int spawnY = y; - if (spawnY > World.MAP_MAX_Y) + if (spawnY > World.WORLD_Y_MAX) { - spawnY = World.MAP_MAX_Y - 5000; + spawnY = World.WORLD_Y_MAX - 5000; } - if (spawnY < World.MAP_MIN_Y) + if (spawnY < World.WORLD_Y_MIN) { - spawnY = World.MAP_MIN_Y + 5000; + spawnY = World.WORLD_Y_MIN + 5000; } // Set the x,y,z position of the WorldObject. If flagged with _isSpawned, setXYZ will automatically update world region, so avoid that. @@ -518,23 +518,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif public void setXYZInvisible(int x, int y, int z) { int correctX = x; - if (correctX > World.MAP_MAX_X) + if (correctX > World.WORLD_X_MAX) { - correctX = World.MAP_MAX_X - 5000; + correctX = World.WORLD_X_MAX - 5000; } - if (correctX < World.MAP_MIN_X) + if (correctX < World.WORLD_X_MIN) { - correctX = World.MAP_MIN_X + 5000; + correctX = World.WORLD_X_MIN + 5000; } int correctY = y; - if (correctY > World.MAP_MAX_Y) + if (correctY > World.WORLD_Y_MAX) { - correctY = World.MAP_MAX_Y - 5000; + correctY = World.WORLD_Y_MAX - 5000; } - if (correctY < World.MAP_MIN_Y) + if (correctY < World.WORLD_Y_MIN) { - correctY = World.MAP_MIN_Y + 5000; + correctY = World.WORLD_Y_MIN + 5000; } setXYZ(correctX, correctY, z); diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/Creature.java index 239a7b60a3..b80d6b5ffd 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -65,8 +65,6 @@ 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.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -2585,7 +2583,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3406,8 +3404,8 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe final int originalX = x; final int originalY = y; final int originalZ = z; - final int gtx = (originalX - World.MAP_MIN_X) >> 4; - final int gty = (originalY - World.MAP_MIN_Y) >> 4; + final int gtx = (originalX - World.WORLD_X_MIN) >> 4; + final int gty = (originalY - World.WORLD_Y_MIN) >> 4; if (isOnGeodataPath()) { try @@ -3430,7 +3428,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe && !(((curZ - z) > 300) && (distance < 300))) // Prohibit correcting destination if character wants to fall. { // location different if destination wasn't reached (or just z coord is different) - final Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(curX, curY, curZ, x, y, z, getInstanceWorld()); + final Location destiny = GeoEngine.getInstance().getValidLocation(curX, curY, curZ, x, y, z, getInstanceWorld()); x = destiny.getX(); y = destiny.getY(); z = destiny.getZ(); @@ -3444,7 +3442,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { if (isPlayer() && !_isFlying && !isInWater) diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/Summon.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/Summon.java index 2aaa8064dd..4b09eca1d7 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/Summon.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/actor/Summon.java @@ -106,7 +106,7 @@ public abstract class Summon extends Playable final int x = owner.getX(); final int y = owner.getY(); final int z = owner.getZ(); - final Location location = GeoEngine.getInstance().canMoveToTargetLoc(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, owner.getInstanceWorld()); + final Location location = GeoEngine.getInstance().getValidLocation(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, getInstanceWorld()); setXYZInvisible(location.getX(), location.getY(), location.getZ()); } diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java index abaec9036f..20cd1966d5 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java @@ -1560,7 +1560,7 @@ public class ItemInstance extends WorldObject if (dropper != null) { final Instance instance = dropper.getInstanceWorld(); - final Location dropDest = GeoEngine.getInstance().canMoveToTargetLoc(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); + final Location dropDest = GeoEngine.getInstance().getValidLocation(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); x = dropDest.getX(); y = dropDest.getY(); z = dropDest.getZ(); diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java index 5d294fe47c..558bf92edf 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java @@ -1179,7 +1179,7 @@ public class SkillCaster implements Runnable } } - final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().canMoveToTargetLoc(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); + final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().getValidLocation(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); creature.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); creature.broadcastPacket(new FlyToLocation(creature, destination, flyType, 0, 0, 333)); diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java index c62c821f44..30068134e3 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java @@ -114,7 +114,7 @@ public class ValidatePosition implements IClientIncomingPacket { if (player.isFalling(_z)) { - final int nearestZ = GeoEngine.getInstance().getHigherHeight(_x, _y, _z); + final int nearestZ = GeoEngine.getInstance().getHeight(_x, _y, _z); if (player.getZ() < nearestZ) { player.setXYZ(_x, _y, nearestZ); diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/util/GeoUtils.java index 4600a0fc53..19a979819e 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -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.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; @@ -30,21 +30,21 @@ public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getInstance().getWorldX(iter.x()); - final int wy = GeoEngine.getInstance().getWorldY(iter.y()); + final int wx = GeoEngine.getWorldX(iter.x()); + final int wy = GeoEngine.getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public final class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); - int wy = GeoEngine.getInstance().getWorldY(prevY); + int wx = GeoEngine.getWorldX(prevX); + int wy = GeoEngine.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.getInstance().getWorldX(curX); - wy = GeoEngine.getInstance().getWorldY(curY); + wx = GeoEngine.getWorldX(curX); + wy = GeoEngine.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().checkNearestNswe(x, y, z, nswe)) + if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0) { return Color.GREEN; } @@ -109,9 +109,8 @@ public final class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final GeoEngine ge = GeoEngine.getInstance(); - final int playerGx = ge.getGeoX(player.getX()); - final int playerGy = ge.getGeoY(player.getY()); + final int playerGx = GeoEngine.getGeoX(player.getX()); + final int playerGy = GeoEngine.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -135,32 +134,32 @@ public final class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = ge.getWorldX(gx); - final int y = ge.getWorldY(gy); - final int z = ge.getNearestZ(gx, gy, player.getZ()); + final int x = GeoEngine.getWorldX(gx); + final int y = GeoEngine.getWorldY(gy); + final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); + Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); 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, Cell.NSWE_EAST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); 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, Cell.NSWE_SOUTH); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); 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, Cell.NSWE_WEST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); 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); @@ -188,41 +187,41 @@ public final class GeoUtils { if (y > lastY) { - return Cell.NSWE_SOUTH_EAST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E; } else if (y < lastY) { - return Cell.NSWE_NORTH_EAST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E; } else { - return Cell.NSWE_EAST; + return GeoStructure.CELL_FLAG_E; } } else if (x < lastX) // west { if (y > lastY) { - return Cell.NSWE_SOUTH_WEST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W; } else if (y < lastY) { - return Cell.NSWE_NORTH_WEST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W; } else { - return Cell.NSWE_WEST; + return GeoStructure.CELL_FLAG_W; } } else // unchanged x { if (y > lastY) { - return Cell.NSWE_SOUTH; + return GeoStructure.CELL_FLAG_S; } else if (y < lastY) { - return Cell.NSWE_NORTH; + return GeoStructure.CELL_FLAG_N; } else { diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/config/GeoEngine.ini b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/config/GeoEngine.ini index 609e8a89a7..e740c678dd 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/config/GeoEngine.ini @@ -6,33 +6,44 @@ # at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ 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 + # ================================================================= -# Path finding +# Pathfinding # ================================================================= # 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 +# an alternative path (e.g. walk around obstacle), default: True +PathFinding = True -# Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 -PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 +# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 -# Weight for nodes without obstacles far from walls -LowWeight = 0.5 +# Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 +MoveWeight = 10 +MoveWeightDiag = 14 -# Weight for nodes near walls -MediumWeight = 2 +# 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 -# Weight for nodes with obstacles -HighWeight = 3 +# 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. -# Default: LowWeight * sqrt(2) -DiagonalWeight = 0.707 +# Maximum number of generated nodes per one path-finding process, default 3500 +MaxIterations = 3500 # ================================================================= -# Other +# Line of Sight # ================================================================= -# Correct player Z after falling through the ground. -CorrectPlayerZ = False +# 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 diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/geodata/L2D_GeoEngine.patch b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/geodata/L2D_GeoEngine.patch deleted file mode 100644 index f8577e3a0e..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/geodata/L2D_GeoEngine.patch +++ /dev/null @@ -1,6035 +0,0 @@ -Index: dist/game/GeoDataConverter.bat -=================================================================== ---- dist/game/GeoDataConverter.bat (nonexistent) -+++ dist/game/GeoDataConverter.bat (working copy) -@@ -0,0 +1,6 @@ -+@echo off -+title L2D geodata converter -+ -+java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter -+ -+pause -Index: dist/game/GeoDataConverter.sh -=================================================================== ---- dist/game/GeoDataConverter.sh (nonexistent) -+++ dist/game/GeoDataConverter.sh (working copy) -@@ -0,0 +1,4 @@ -+#! /bin/sh -+ -+java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 -+ -Index: dist/game/config/GeoEngine.ini -=================================================================== ---- dist/game/config/GeoEngine.ini (revision 8409) -+++ dist/game/config/GeoEngine.ini (working copy) -@@ -1,6 +1,10 @@ - # ================================================================= - # Geodata - # ================================================================= -+# Because of real-time performance we are using geodata files only in -+# diagonal L2D format now (using filename e.g. 22_16.l2d). -+# L2D geodata can be obtained by conversion of existing L2J or L2OFF geodata. -+# Launch "GeoDataConverter.bat/sh" and follow instructions to start the conversion. - - # 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/ -@@ -13,6 +17,16 @@ - CoordSynchronize = 2 - - # ================================================================= -+# Path checking -+# ================================================================= -+ -+# 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 finding - # ================================================================= - -@@ -23,19 +37,23 @@ - # Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - --# Weight for nodes without obstacles far from walls --LowWeight = 0.5 -+# Base path weight, when moving from one node to another on axis direction, default: 10 -+BaseWeight = 10 - --# Weight for nodes near walls --MediumWeight = 2 -+# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -+DiagonalWeight = 14 - --# Weight for nodes with obstacles --HighWeight = 3 -+# When movement flags of target node is blocked to any direction, multiply movement weight by this multiplier. -+# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 10 -+ObstacleMultiplier = 10 - --# Weight for diagonal movement. --# Default: LowWeight * sqrt(2) --DiagonalWeight = 0.707 -+# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 20 -+# For proper function must be higher than BaseWeight and/or DiagonalWeight. -+HeuristicWeight = 20 - -+# Maximum number of generated nodes per one path-finding process, default 3500 -+MaxIterations = 3500 -+ - # ================================================================= - # Other - # ================================================================= -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (working copy) -@@ -55,9 +55,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -@@ -73,9 +72,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (working copy) -@@ -18,11 +18,11 @@ - - import java.util.List; - --import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -+import org.l2jmobius.gameserver.geoengine.GeoEngine; - import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -+import org.l2jmobius.gameserver.network.SystemMessageId; - import org.l2jmobius.gameserver.util.BuilderUtil; - - public class AdminPathNode implements IAdminCommandHandler -@@ -37,29 +37,30 @@ - { - if (command.equals("admin_path_find")) - { -- if (!Config.PATHFINDING) -- { -- BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); -- return true; -- } - if (activeChar.getTarget() != null) - { -- final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); -+ final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); - if (path == null) - { -- BuilderUtil.sendSysMessage(activeChar, "No Route!"); -- return true; -+ BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); - } -- for (AbstractNodeLoc a : path) -+ else - { -- BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); -+ for (Location point : path) -+ { -+ BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); -+ } - } - } - else - { -- BuilderUtil.sendSysMessage(activeChar, "No Target!"); -+ activeChar.sendPacket(SystemMessageId.INVALID_TARGET); - } - } -+ else -+ { -+ return false; -+ } - return true; - } - -Index: java/org/l2jmobius/Config.java -=================================================================== ---- java/org/l2jmobius/Config.java (revision 8409) -+++ java/org/l2jmobius/Config.java (working copy) -@@ -32,7 +32,6 @@ - import java.net.UnknownHostException; - import java.nio.charset.StandardCharsets; - import java.nio.file.Files; --import java.nio.file.Path; - import java.nio.file.Paths; - import java.time.Duration; - import java.util.ArrayList; -@@ -966,14 +965,17 @@ - // -------------------------------------------------- - // GeoEngine - // -------------------------------------------------- -- public static Path GEODATA_PATH; -+ public static String GEODATA_PATH; -+ public static int COORD_SYNCHRONIZE; -+ public static int PART_OF_CHARACTER_HEIGHT; -+ public static int MAX_OBSTACLE_HEIGHT; - public static boolean PATHFINDING; - public static String PATHFIND_BUFFERS; -- public static float LOW_WEIGHT; -- public static float MEDIUM_WEIGHT; -- public static float HIGH_WEIGHT; -- public static float DIAGONAL_WEIGHT; -- public static int COORD_SYNCHRONIZE; -+ public static int BASE_WEIGHT; -+ public static int DIAGONAL_WEIGHT; -+ public static int HEURISTIC_WEIGHT; -+ public static int OBSTACLE_MULTIPLIER; -+ public static int MAX_ITERATIONS; - public static boolean CORRECT_PLAYER_Z; - - /** Attribute System */ -@@ -2568,14 +2570,17 @@ - - // Load GeoEngine config file (if exists) - final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); -- GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); -+ GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); -+ COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); -+ MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); - PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -- LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); -- MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); -- HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); -- DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); -- COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); -+ DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); -+ OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); -+ HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); -+ MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); - CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); - - // Load AllowedPlayerRaces config file (if exists) -Index: java/org/l2jmobius/gameserver/geoengine/GeoEngine.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (revision 8435) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (working copy) -@@ -16,100 +16,90 @@ - */ - package org.l2jmobius.gameserver.geoengine; - --import java.io.IOException; -+import java.io.File; - import java.io.RandomAccessFile; - import java.nio.ByteOrder; --import java.nio.channels.FileChannel.MapMode; --import java.nio.file.Files; --import java.nio.file.Path; --import java.util.concurrent.atomic.AtomicReferenceArray; --import java.util.logging.Level; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.List; - import java.util.logging.Logger; - - import org.l2jmobius.Config; - import org.l2jmobius.gameserver.data.xml.DoorData; - import org.l2jmobius.gameserver.data.xml.FenceData; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; --import org.l2jmobius.gameserver.geoengine.geodata.IRegion; --import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; --import org.l2jmobius.gameserver.geoengine.geodata.Region; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.World; - import org.l2jmobius.gameserver.model.WorldObject; -+import org.l2jmobius.gameserver.model.actor.Creature; - import org.l2jmobius.gameserver.model.instancezone.Instance; --import org.l2jmobius.gameserver.model.interfaces.ILocational; --import org.l2jmobius.gameserver.util.GeoUtils; --import org.l2jmobius.gameserver.util.LinePointIterator; --import org.l2jmobius.gameserver.util.LinePointIterator3D; -+import org.l2jmobius.gameserver.util.MathUtil; - - /** -- * @author -Nemesiss-, HorridoJoho -+ * @author Hasha - */ - public class GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); -+ protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - -- private static final int WORLD_MIN_X = -655360; -- private static final int WORLD_MIN_Y = -589824; -- private static final int WORLD_MIN_Z = -16384; -+ private final ABlock[][] _blocks; -+ private final BlockNull _nullBlock; - -- /** 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; -- -- /** The regions array */ -- private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); -- -- private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; -- private static final int ELEVATED_SEE_OVER_DISTANCE = 2; -- private static final int MAX_SEE_OVER_HEIGHT = 48; -- -- protected GeoEngine() -+ /** -+ * GeoEngine constructor. Loads all geodata files of chosen geodata format. -+ */ -+ public GeoEngine() - { - LOGGER.info("GeoEngine: Initializing..."); -- for (int i = 0; i < _regions.length(); i++) -- { -- _regions.set(i, NullRegion.INSTANCE); -- } - -+ // initialize block container -+ _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; -+ -+ // load null block -+ _nullBlock = new BlockNull(); -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files according to geoengine config setup - int loaded = 0; -- try -+ long fileSize = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { -- for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { -- for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) -+ final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); -+ if (f.exists() && !f.isDirectory()) - { -- final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); -- if (Files.exists(geoFilePath)) -+ // region file is load-able, try to load it -+ if (loadGeoBlocks(rx, ry)) - { -- try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) -- { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -- loaded++; -- } -+ loaded++; -+ fileSize += f.length(); - } - } -+ else -+ { -+ // region file is not load-able, load null blocks -+ loadNullBlocks(rx, ry); -+ } - } - } -- catch (Exception e) -+ -+ LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -+ if (loaded > 0) - { -- LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); -- System.exit(1); -+ LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); - } - -- LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -- -- // Avoid wrong configs when no files are loaded. -+ // avoid wrong configs when no files are loaded - if (loaded == 0) - { - if (Config.PATHFINDING) -@@ -123,619 +113,820 @@ - LOGGER.info("GeoEngine: Forcing CoordSynchronize setting to -1."); - } - } -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); - } - - /** -- * @param geoX -- * @param geoY -- * @return the region -+ * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. -+ * @return boolean : True, when geodata file was loaded without problem. - */ -- private IRegion getRegion(int geoX, int geoY) -+ private final boolean loadGeoBlocks(int regionX, int regionY) - { -- final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); -- if ((region < 0) || (region >= _regions.length())) -+ final String filename = String.format(GeoFormat.L2D.getFilename(), regionX, regionY); -+ -+ // standard load -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) - { -- return null; -+ // initialize file buffer -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, GeoFormat.L2D); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ } -+ -+ // check data consistency -+ if (buffer.remaining() > 0) -+ { -+ LOGGER.warning("GeoEngine: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ } -+ -+ // loading was successful -+ return true; - } -- return _regions.get(region); -- } -- -- /** -- * @param filePath -- * @param regionX -- * @param regionY -- * @throws IOException -- */ -- 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")) -+ catch (Exception e) - { -- _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -+ // an error occured while loading, load null blocks -+ LOGGER.warning("GeoEngine: Error while loading " + filename + " region file."); -+ LOGGER.warning(e.getMessage()); -+ -+ // replace whole region file with null blocks -+ loadNullBlocks(regionX, regionY); -+ -+ // loading was not successful -+ return false; - } - } - - /** -- * @param regionX -- * @param regionY -+ * Loads null blocks. Used when no region file is detected or an error occurs during loading. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. - */ -- public void unloadRegion(int regionX, int regionY) -+ private final void loadNullBlocks(int regionX, int regionY) - { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); -- } -- -- /** -- * @param geoX -- * @param geoY -- * @return if geodata exist -- */ -- public boolean hasGeoPos(int geoX, int geoY) -- { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // load all null blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { -- return false; -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[blockX + ix][blockY + iy] = _nullBlock; -+ } - } -- return region.hasGeo(); - } - -+ // GEODATA - GENERAL -+ - /** -- * Checks the specified position for available geodata. -- * @param x the world x -- * @param y the world y -- * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise -+ * Converts world X to geodata X. -+ * @param worldX -+ * @return int : Geo X - */ -- public boolean hasGeo(int x, int y) -+ public static int getGeoX(int worldX) - { -- return hasGeoPos(getGeoX(x), getGeoY(y)); -+ return (MathUtil.limit(worldX, World.MAP_MIN_X, World.MAP_MAX_X) - World.MAP_MIN_X) >> 4; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe check -+ * Converts world Y to geodata Y. -+ * @param worldY -+ * @return int : Geo Y - */ -- public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) -+ public static int getGeoY(int worldY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return true; -- } -- return region.checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(worldY, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.MAP_MIN_Y) >> 4; - } - - /** -+ * Converts geodata X to world X. - * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe anti-corner cut -+ * @return int : World X - */ -- public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) -+ public static int getWorldX(int geoX) - { -- boolean can = true; -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- return can && checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(geoX, 0, GeoStructure.GEO_CELLS_X) << 4) + World.MAP_MIN_X + 8; - } - - /** -- * @param geoX -+ * Converts geodata Y to world Y. - * @param geoY -- * @param worldZ -- * @return the nearest Z value -+ * @return int : World Y - */ -- public int getNearestZ(int geoX, int geoY, int worldZ) -+ public static int getWorldY(int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return worldZ; -- } -- return region.getNearestZ(geoX, geoY, worldZ); -+ return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next lower Z value -+ * Returns block of geodata on given coordinates. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return {@link ABlock} : Block of geodata. - */ -- public int getNextLowerZ(int geoX, int geoY, int worldZ) -+ private final ABlock getBlock(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final int x = geoX / GeoStructure.BLOCK_CELLS_X; -+ final int y = geoY / GeoStructure.BLOCK_CELLS_Y; -+ -+ // if x or y is out of array return null -+ if ((x > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) - { -- return worldZ; -+ return _blocks[x][y]; - } -- return region.getNextLowerZ(geoX, geoY, worldZ); -+ return null; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next higher Z value -+ * Check if geo coordinates has geo. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return boolean : True, if given geo coordinates have geodata - */ -- public int getNextHigherZ(int geoX, int geoY, int worldZ) -+ public boolean hasGeoPos(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final ABlock block = getBlock(geoX, geoY); -+ if (block == null) // null block check - { -- return worldZ; -+ // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) -+ // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); -+ return false; - } -- return region.getNextHigherZ(geoX, geoY, worldZ); -+ return block.hasGeoPos(); - } - - /** -- * Gets the Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the height of cell, which is closest to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, closest to given coordinates. - */ -- public int getHeight(int x, int y, int z) -+ public short getHeightNearest(int geoX, int geoY, int worldZ) - { -- return getNearestZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getHeightNearest(geoX, geoY, worldZ) : (short) worldZ; - } - - /** -- * Gets the next lower Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the NSWE flag byte of cell, which is closes to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. - */ -- public int getLowerHeight(int x, int y, int z) -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) - { -- return getNextLowerZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getNsweNearest(geoX, geoY, worldZ) : (byte) 0xFF; - } - - /** -- * Gets the next higher Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Check if world coordinates has geo. -+ * @param worldX : World X -+ * @param worldY : World Y -+ * @return boolean : True, if given world coordinates have geodata - */ -- public int getHigherHeight(int x, int y, int z) -+ public boolean hasGeo(int worldX, int worldY) - { -- return getNextHigherZ(getGeoX(x), getGeoY(y), z); -+ return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); - } - - /** -- * @param worldX -- * @return the geo X -+ * Returns closest Z coordinate according to geodata. -+ * @param worldX : world x -+ * @param worldY : world y -+ * @param worldZ : world z -+ * @return short : nearest Z coordinates according to geodata - */ -- public int getGeoX(int worldX) -+ public short getHeight(int worldX, int worldY, int worldZ) - { -- return (worldX - WORLD_MIN_X) / 16; -+ return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - -- /** -- * @param worldY -- * @return the geo Y -- */ -- public int getGeoY(int worldY) -- { -- return (worldY - WORLD_MIN_Y) / 16; -- } -+ // PATHFINDING - - /** -- * @param worldZ -- * @return the geo Z -+ * Check line of sight from {@link WorldObject} to {@link WorldObject}. -+ * @param origin : The origin object. -+ * @param target : The target object. -+ * @return {@code boolean} : True if origin can see target - */ -- public int getGeoZ(int worldZ) -+ public boolean canSeeTarget(WorldObject origin, WorldObject target) - { -- return (worldZ - WORLD_MIN_Z) / 16; -- } -- -- /** -- * @param geoX -- * @return the world X -- */ -- public int getWorldX(int geoX) -- { -- return (geoX * 16) + WORLD_MIN_X + 8; -- } -- -- /** -- * @param geoY -- * @return the world Y -- */ -- public int getWorldY(int geoY) -- { -- return (geoY * 16) + WORLD_MIN_Y + 8; -- } -- -- /** -- * @param geoZ -- * @return the world Z -- */ -- public int getWorldZ(int geoZ) -- { -- return (geoZ * 16) + WORLD_MIN_Z + 8; -- } -- -- /** -- * Can see target. Doors as target always return true. Checks doors between. -- * @param cha the character -- * @param target the target -- * @return {@code true} if the character can see the target (LOS), {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, WorldObject target) -- { -- if (target.isDoor()) -+ if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) - { -- // Can always see doors. - return true; - } -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param cha the character -- * @param worldPosition the world position -- * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) -- { -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param world -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @param tworld the target's instanceId -- * @return -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) -- { -- return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instance -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) -- { -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) -+ -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = target.getX(); -+ final int ty = target.getY(); -+ final int tz = target.getZ(); -+ -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } -- return canSeeTarget(x, y, z, tx, ty, tz); -- } -- -- /** -- * @param prevX -- * @param prevY -- * @param prevGeoZ -- * @param curX -- * @param curY -- * @param nswe -- * @return the LOS Z value -- */ -- private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) -- { -- if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { -- throw new RuntimeException("Multiple directions!"); -+ return false; - } - -- if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- return getNearestZ(curX, curY, prevGeoZ); -+ return true; - } - -- return getNextHigherZ(curX, curY, prevGeoZ); -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) -+ { -+ return true; -+ } -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) -+ { -+ return goz == gtz; -+ } -+ -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) -+ { -+ oheight = ((Creature) origin).getCollisionHeight() * 2; -+ } -+ -+ double theight = 0; -+ if (target.isCreature()) -+ { -+ theight = ((Creature) target).getCollisionHeight() * 2; -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); - } - - /** -- * Can see target. Does not check doors between. -- * @param xValue the x coordinate -- * @param yValue the y coordinate -- * @param zValue the z coordinate -- * @param txValue the target's x coordinate -- * @param tyValue the target's y coordinate -- * @param tzValue the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -+ * Check line of sight from {@link WorldObject} to {@link Location}. -+ * @param origin : The origin object. -+ * @param position : The target position. -+ * @return {@code boolean} : True if object can see position - */ -- public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) -+ public boolean canSeeTarget(WorldObject origin, Location position) - { -- int x = xValue; -- int y = yValue; -- int tx = txValue; -- int ty = tyValue; -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = position.getX(); -+ final int ty = position.getY(); -+ final int tz = position.getZ(); - -- int geoX = getGeoX(x); -- int geoY = getGeoY(y); -- int tGeoX = getGeoX(tx); -- int tGeoY = getGeoY(ty); -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) -+ { -+ return false; -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) -+ { -+ return false; -+ } - -- int z = getNearestZ(geoX, geoY, zValue); -- int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- // Fastpath. -- if ((geoX == tGeoX) && (geoY == tGeoY)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- if (hasGeoPos(tGeoX, tGeoY)) -- { -- return z == tz; -- } - return true; - } - -- if (tz > z) -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) - { -- int tmp = tx; -- tx = x; -- x = tmp; -- -- tmp = ty; -- ty = y; -- y = tmp; -- -- tmp = tz; -- tz = z; -- z = tmp; -- -- tmp = tGeoX; -- tGeoX = geoX; -- geoX = tmp; -- -- tmp = tGeoY; -- tGeoY = geoY; -- geoY = tmp; -+ return goz == gtz; - } - -- final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); -- // First point is guaranteed to be available, skip it, we can always see our own position. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- final int prevZ = pointIter.z(); -- int prevGeoZ = prevZ; -- int ptIndex = 0; -- while (pointIter.next()) -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight(); -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); -+ } -+ -+ /** -+ * Simple check for origin to target visibility. -+ * @param goxValue : origin X geodata coordinate -+ * @param goyValue : origin Y geodata coordinate -+ * @param gozValue : origin Z geodata coordinate -+ * @param oheight : origin height (if instance of {@link Character}) -+ * @param gtxValue : target X geodata coordinate -+ * @param gtyValue : target Y geodata coordinate -+ * @param gtzValue : target Z geodata coordinate -+ * @param theight : target height (if instance of {@link Character}) -+ * @param instance -+ * @return {@code boolean} : True, when target can be seen. -+ */ -+ private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) -+ { -+ int goz = gozValue; -+ int gtz = gtzValue; -+ int gox = goxValue; -+ int goy = goyValue; -+ int gtx = gtxValue; -+ int gty = gtyValue; -+ -+ // get line of sight Z coordinates -+ double losoz = goz + ((oheight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ double lostz = gtz + ((theight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ -+ // get X delta and signum -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirox = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; -+ final byte dirtx = sx > 0 ? GeoStructure.CELL_FLAG_W : GeoStructure.CELL_FLAG_E; -+ -+ // get Y delta and signum -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte diroy = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ final byte dirty = sy > 0 ? GeoStructure.CELL_FLAG_N : GeoStructure.CELL_FLAG_S; -+ -+ // get Z delta -+ final int dm = Math.max(dx, dy); -+ final double dz = (lostz - losoz) / dm; -+ -+ // get direction flag for diagonal movement -+ final byte diroxy = getDirXY(dirox, diroy); -+ final byte dirtxy = getDirXY(dirtx, dirty); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte diro; -+ byte dirt; -+ -+ // initialize node values -+ int nox = gox; -+ int noy = goy; -+ int ntx = gtx; -+ int nty = gty; -+ byte nsweo = getNsweNearest(gox, goy, goz); -+ byte nswet = getNsweNearest(gtx, gty, gtz); -+ -+ // loop -+ ABlock block; -+ int index; -+ for (int i = 0; i < ((dm + 1) / 2); i++) -+ { -+ // reset direction flag -+ diro = 0; -+ dirt = 0; - -- if ((curX == prevX) && (curY == prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- continue; -+ // calculate next point XY coordinates -+ d -= dy; -+ d += dx; -+ nox += sx; -+ ntx -= sx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroxy; -+ dirt |= dirtxy; - } -+ else if (e2 > -dy) -+ { -+ // calculate next point X coordinate -+ d -= dy; -+ nox += sx; -+ ntx -= sx; -+ diro |= dirox; -+ dirt |= dirtx; -+ } -+ else if (e2 < dx) -+ { -+ // calculate next point Y coordinate -+ d += dx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroy; -+ dirt |= dirty; -+ } - -- final int beeCurZ = pointIter.z(); -- int curGeoZ = prevGeoZ; -- -- // Check if the position has geodata. -- if (hasGeoPos(curX, curY)) - { -- final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); -- curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); -- int maxHeight; -- if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) -+ // get block of the next cell -+ block = getBlock(nox, noy); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nsweo & diro) == 0) - { -- maxHeight = z + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); - } - else - { -- maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); - } - -- boolean canSeeThrough = false; -- if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) -+ // layer does not exist, return -+ if (index == -1) - { -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else -- { -- canSeeThrough = true; -- } -+ return false; - } - -- if (!canSeeThrough) -+ // get layer and next line of sight Z coordinate -+ goz = block.getHeight(index); -+ losoz += dz; -+ -+ // perform line of sight check, return when fails -+ if ((goz - losoz) > Config.MAX_OBSTACLE_HEIGHT) - { - return false; - } -+ -+ // get layer nswe -+ nsweo = block.getNswe(index); - } -+ { -+ // get block of the next cell -+ block = getBlock(ntx, nty); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nswet & dirt) == 0) -+ { -+ index = block.getIndexAbove(ntx, nty, gtz - GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ else -+ { -+ index = block.getIndexBelow(ntx, nty, gtz + GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ -+ // layer does not exist, return -+ if (index == -1) -+ { -+ return false; -+ } -+ -+ // get layer and next line of sight Z coordinate -+ gtz = block.getHeight(index); -+ lostz -= dz; -+ -+ // perform line of sight check, return when fails -+ if ((gtz - lostz) > Config.MAX_OBSTACLE_HEIGHT) -+ { -+ return false; -+ } -+ -+ // get layer nswe -+ nswet = block.getNswe(index); -+ } - -- prevX = curX; -- prevY = curY; -- prevGeoZ = curGeoZ; -- ++ptIndex; -+ // update coords -+ gox = nox; -+ goy = noy; -+ gtx = ntx; -+ gty = nty; - } - -- return true; -+ // when iteration is completed, compare final Z coordinates -+ return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); - } - - /** -- * Move check. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param zValue the z coordinate -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tzValue the target's z coordinate -- * @param instance the instance -- * @return the last Location (x,y,z) where player can walk - just before wall -+ * Check movement from coordinates to coordinates. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {code boolean} : True if target coordinates are reachable from origin coordinates - */ -- public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) -+ public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- final int geoX = getGeoX(x); -- final int geoY = getGeoY(y); -- final int z = getNearestZ(geoX, geoY, zValue); -- final int tGeoX = getGeoX(tx); -- final int tGeoY = getGeoY(ty); -- final int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } -- if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = z; -+ // perform geodata check -+ final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); -+ return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); -+ } -+ -+ /** -+ * Check movement from origin to target. Returns last available point in the checked path. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {@link Location} : Last point where object can walk (just before wall) -+ */ -+ public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) -+ { -+ return new Location(ox, oy, oz); -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) -+ { -+ return new Location(ox, oy, oz); -+ } - -- while (pointIter.next()) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -- -- if (hasGeoPos(prevX, prevY)) -- { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -- { -- // Can't move, return previous location. -- return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); -- } -- } -- -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return new Location(tx, ty, tz); - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != tz)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- // Different floors, return start location. -- return new Location(x, y, z); -+ return new Location(tx, ty, tz); - } - -- return new Location(tx, ty, tz); -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) -+ { -+ return new Location(tx, ty, tz); -+ } -+ -+ // perform geodata check -+ return checkMove(gox, goy, goz, gtx, gty, gtz, instance); - } - - /** -- * Checks if its possible to move from one location to another. -- * @param fromX the X coordinate to start checking from -- * @param fromY the Y coordinate to start checking from -- * @param fromZvalue the Z coordinate to start checking from -- * @param toX the X coordinate to end checking at -- * @param toY the Y coordinate to end checking at -- * @param toZvalue the Z coordinate to end checking at -- * @param instance the instance -- * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise -+ * With this method you can check if a position is visible or can be reached by beeline movement.
-+ * Target X and Y reachable and Z is on same floor: -+ *
    -+ *
  • Location of the target with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y reachable but Z is on another floor: -+ *
    -+ *
  • Location of the origin with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y not reachable: -+ *
    -+ *
  • Last accessible location in destination to target.
  • -+ *
-+ * @param gox : origin X geodata coordinate -+ * @param goy : origin Y geodata coordinate -+ * @param goz : origin Z geodata coordinate -+ * @param gtx : target X geodata coordinate -+ * @param gty : target Y geodata coordinate -+ * @param gtz : target Z geodata coordinate -+ * @param instance -+ * @return {@link GeoLocation} : The last allowed point of movement. - */ -- public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) -+ protected final GeoLocation checkMove(int gox, int goy, int goz, int gtx, int gty, int gtz, Instance instance) - { -- final int geoX = getGeoX(fromX); -- final int geoY = getGeoY(fromY); -- final int fromZ = getNearestZ(geoX, geoY, fromZvalue); -- final int tGeoX = getGeoX(toX); -- final int tGeoY = getGeoY(toY); -- final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); -- -- if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) -+ if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } -- if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) -+ if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = fromZ; -+ // get X delta, signum and direction flag -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirX = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; - -- while (pointIter.next()) -+ // get Y delta, signum and direction flag -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte dirY = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ -+ // get direction flag for diagonal movement -+ final byte dirXY = getDirXY(dirX, dirY); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte direction; -+ -+ // load pointer coordinates -+ int gpx = gox; -+ int gpy = goy; -+ int gpz = goz; -+ -+ // load next pointer -+ int nx = gpx; -+ int ny = gpy; -+ -+ // loop -+ int count = 0; -+ while (count++ < Config.MAX_ITERATIONS) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -+ direction = 0; - -- if (hasGeoPos(prevX, prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -+ d -= dy; -+ d += dx; -+ nx += sx; -+ ny += sy; -+ direction |= dirXY; -+ } -+ else if (e2 > -dy) -+ { -+ d -= dy; -+ nx += sx; -+ direction |= dirX; -+ } -+ else if (e2 < dx) -+ { -+ d += dx; -+ ny += sy; -+ direction |= dirY; -+ } -+ -+ // obstacle found, return -+ if ((getNsweNearest(gpx, gpy, gpz) & direction) == 0) -+ { -+ return new GeoLocation(gpx, gpy, gpz); -+ } -+ -+ // update pointer coordinates -+ gpx = nx; -+ gpy = ny; -+ gpz = getHeightNearest(nx, ny, gpz); -+ -+ // target coordinates reached -+ if ((gpx == gtx) && (gpy == gty)) -+ { -+ if (gpz == gtz) - { -- return false; -+ // path found, Z coordinates are okay, return target point -+ return new GeoLocation(gtx, gty, gtz); - } -+ -+ // path found, Z coordinates are not okay, return last good point -+ return new GeoLocation(gpx, gpy, gpz); - } -+ } -+ -+ return new GeoLocation(gox, goy, goz); -+ } -+ -+ /** -+ * Returns diagonal NSWE flag format of combined two NSWE flags. -+ * @param dirX : X direction NSWE flag -+ * @param dirY : Y direction NSWE flag -+ * @return byte : NSWE flag of combined direction -+ */ -+ private static byte getDirXY(byte dirX, byte dirY) -+ { -+ // check axis directions -+ if (dirY == GeoStructure.CELL_FLAG_N) -+ { -+ if (dirX == GeoStructure.CELL_FLAG_W) -+ { -+ return GeoStructure.CELL_FLAG_NW; -+ } - -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return GeoStructure.CELL_FLAG_NE; - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) -+ if (dirX == GeoStructure.CELL_FLAG_W) - { -- // Different floors. -- return false; -+ return GeoStructure.CELL_FLAG_SW; - } - -- return true; -+ return GeoStructure.CELL_FLAG_SE; - } - -+ /** -+ * Returns the list of location objects as a result of complete path calculation. -+ * @param ox : origin x -+ * @param oy : origin y -+ * @param oz : origin z -+ * @param tx : target x -+ * @param ty : target y -+ * @param tz : target z -+ * @param instance -+ * @return {@code List} : complete path from nodes -+ */ -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ return null; -+ } -+ -+ /** -+ * Returns the instance of the {@link GeoEngine}. -+ * @return {@link GeoEngine} : The instance. -+ */ - public static GeoEngine getInstance() - { - return SingletonHolder.INSTANCE; -@@ -743,6 +934,6 @@ - - private static class SingletonHolder - { -- protected static final GeoEngine INSTANCE = new GeoEngine(); -+ protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); - } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (working copy) -@@ -20,88 +20,84 @@ - import java.util.LinkedList; - import java.util.List; - import java.util.ListIterator; --import java.util.logging.Level; --import java.util.logging.Logger; - - import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; --import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; --import org.l2jmobius.gameserver.model.World; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -+import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.instancezone.Instance; - - /** -- * @author -Nemesiss- -+ * @author Hasha - */ --public class GeoEnginePathfinding -+final class GeoEnginePathfinding extends GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); -+ // pre-allocated buffers -+ private final BufferHolder[] _buffers; - -- private BufferInfo[] _buffers; -- - protected GeoEnginePathfinding() - { -- try -+ super(); -+ -+ final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ _buffers = new BufferHolder[array.length]; -+ int count = 0; -+ for (int i = 0; i < array.length; i++) - { -- final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ final String buf = array[i]; -+ final String[] args = buf.split("x"); - -- _buffers = new BufferInfo[array.length]; -- -- String buf; -- String[] args; -- for (int i = 0; i < array.length; i++) -+ try - { -- buf = array[i]; -- args = buf.split("x"); -- if (args.length != 2) -- { -- throw new Exception("Invalid buffer definition: " + buf); -- } -- -- _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); -+ final int size = Integer.parseInt(args[1]); -+ count += size; -+ _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); - } -+ catch (Exception e) -+ { -+ LOGGER.warning("GeoEnginePathfinding: Can not load buffer setting: " + buf); -+ } - } -- catch (Exception e) -- { -- LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); -- throw new Error("CellPathFinding: load aborted"); -- } -+ -+ LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); - } - -- public boolean pathNodesExist(short regionoffset) -+ @Override -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- return false; -- } -- -- public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) -- { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -- if (!GeoEngine.getInstance().hasGeo(x, y)) -+ // get origin and check existing geo coords -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { - 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)) -+ -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coords -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { - 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)))); -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // Prepare buffer for pathfinding calculations -+ final NodeBuffer buffer = getBuffer(64 + (2 * Math.max(Math.abs(gox - gtx), Math.abs(goy - gty)))); - if (buffer == null) - { - return null; - } - -- List path = null; -+ // find path -+ List path = null; - try - { -- final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); -- -+ final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); - if (result == null) - { - return null; -@@ -125,33 +121,45 @@ - return path; - } - -- int currentX, currentY, currentZ; -- ListIterator middlePoint; -+ // get path list iterator -+ final ListIterator point = path.listIterator(); - -- middlePoint = path.listIterator(); -- currentX = x; -- currentY = y; -- currentZ = z; -+ // get node A (origin) -+ int nodeAx = gox; -+ int nodeAy = goy; -+ short nodeAz = goz; - -- while (middlePoint.hasNext()) -+ // get node B -+ GeoLocation nodeB = (GeoLocation) point.next(); -+ -+ // iterate thought the path to optimize it -+ int count = 0; -+ while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) - { -- final AbstractNodeLoc locMiddle = middlePoint.next(); -- if (!middlePoint.hasNext()) -- { -- break; -- } -+ // get node C -+ final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - -- final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); -- if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) -+ // check movement from node A to node C -+ final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); -+ if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) - { -- middlePoint.remove(); -+ // can move from node A to node C -+ -+ // remove node B -+ point.remove(); - } - else - { -- currentX = locMiddle.getX(); -- currentY = locMiddle.getY(); -- currentZ = locMiddle.getZ(); -+ // can not move from node A to node C -+ -+ // set node A (node B is part of path, update A coordinates) -+ nodeAx = nodeB.getGeoX(); -+ nodeAy = nodeB.getGeoY(); -+ nodeAz = (short) nodeB.getZ(); - } -+ -+ // set node B -+ nodeB = (GeoLocation) point.next(); - } - - return path; -@@ -158,144 +166,102 @@ - } - - /** -- * Convert geodata position to pathnode position -- * @param geo_pos -- * @return pathnode position -+ * Create list of node locations as result of calculated buffer node tree. -+ * @param node : the entry point -+ * @return List : list of node location - */ -- public short getNodePos(int geo_pos) -+ private static List constructPath(Node node) - { -- return (short) (geo_pos >> 3); // OK? -- } -- -- /** -- * Convert node position to pathnode block position -- * @param node_pos -- * @return pathnode block position (0...255) -- */ -- public short getNodeBlock(int node_pos) -- { -- return (short) (node_pos % 256); -- } -- -- public byte getRegionX(int node_pos) -- { -- return (byte) ((node_pos >> 8) + World.TILE_X_MIN); -- } -- -- public byte getRegionY(int node_pos) -- { -- return (byte) ((node_pos >> 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 node_x rx -- * @return -- */ -- public int calculateWorldX(short node_x) -- { -- return World.MAP_MIN_X + (node_x * 128) + 48; -- } -- -- /** -- * Convert pathnode y to World y position -- * @param node_y -- * @return -- */ -- public int calculateWorldY(short node_y) -- { -- return World.MAP_MIN_Y + (node_y * 128) + 48; -- } -- -- private List constructPath(AbstractNode nodeValue) -- { -- final LinkedList path = new LinkedList<>(); -- int previousDirectionX = Integer.MIN_VALUE; -- int previousDirectionY = Integer.MIN_VALUE; -- int directionX, directionY; -+ // create empty list -+ final LinkedList list = new LinkedList<>(); - -- AbstractNode node = nodeValue; -- while (node.getParent() != null) -+ // set direction X/Y -+ int dx = 0; -+ int dy = 0; -+ -+ // get target parent -+ Node target = node; -+ Node parent = target.getParent(); -+ -+ // while parent exists -+ int count = 0; -+ while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { -- directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); -- directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); -+ // get parent <> target direction X/Y -+ final int nx = parent.getLoc().getGeoX() - target.getLoc().getGeoX(); -+ final int ny = parent.getLoc().getGeoY() - target.getLoc().getGeoY(); - -- // only add a new route point if moving direction changes -- if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) -+ // direction has changed? -+ if ((dx != nx) || (dy != ny)) - { -- previousDirectionX = directionX; -- previousDirectionY = directionY; -+ // add node to the beginning of the list -+ list.addFirst(target.getLoc()); - -- path.addFirst(node.getLoc()); -- node.setLoc(null); -+ // update direction X/Y -+ dx = nx; -+ dy = ny; - } - -- node = node.getParent(); -+ // move to next node, set target and get its parent -+ target = parent; -+ parent = target.getParent(); - } - -- return path; -+ // return list -+ return list; - } - -- private CellNodeBuffer alloc(int size) -+ /** -+ * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer. -+ * @param size : pre-calculated minimal required size -+ * @return NodeBuffer : buffer -+ */ -+ private final NodeBuffer getBuffer(int size) - { -- CellNodeBuffer current = null; -- for (BufferInfo i : _buffers) -+ NodeBuffer current = null; -+ for (BufferHolder holder : _buffers) - { -- if (i.mapSize >= size) -+ // Find proper size of buffer -+ if (holder._size < size) - { -- for (CellNodeBuffer buf : i.buffer) -+ continue; -+ } -+ -+ // Find unlocked NodeBuffer -+ for (NodeBuffer buffer : holder._buffer) -+ { -+ if (!buffer.isLocked()) - { -- if (buf.lock()) -- { -- current = buf; -- break; -- } -+ continue; - } -- if (current != null) -- { -- break; -- } - -- // not found, allocate temporary buffer -- current = new CellNodeBuffer(i.mapSize); -- current.lock(); -- if (i.buffer.size() < i.count) -- { -- i.buffer.add(current); -- break; -- } -+ return buffer; - } -+ -+ // NodeBuffer not found, allocate temporary buffer -+ current = new NodeBuffer(holder._size); -+ current.isLocked(); - } - - return current; - } - -- private static final class BufferInfo -+ /** -+ * NodeBuffer container with specified size and count of separate buffers. -+ */ -+ private static final class BufferHolder - { -- final int mapSize; -- final int count; -- ArrayList buffer; -+ final int _size; -+ List _buffer; - -- public BufferInfo(int size, int cnt) -+ public BufferHolder(int size, int count) - { -- mapSize = size; -- count = cnt; -- buffer = new ArrayList<>(count); -+ _size = size; -+ _buffer = new ArrayList<>(count); -+ for (int i = 0; i < count; i++) -+ { -+ _buffer.add(new NodeBuffer(size)); -+ } - } - } -- -- public static GeoEnginePathfinding getInstance() -- { -- return SingletonHolder.INSTANCE; -- } -- -- private static class SingletonHolder -- { -- protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); -- } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (working copy) -@@ -0,0 +1,197 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @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 height of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getHeightNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first above given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, above given coordinates. -+ */ -+ public abstract short getHeightAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first below given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is closest to given coordinates. -+ * @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 the NSWE flag byte of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getNsweNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first above given coordinates. -+ * @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 getNsweAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first below given coordinates. -+ * @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 getNsweBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is closes layer to given coordinates. -+ * @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. -+ * @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 above given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is first layer below given coordinates. -+ * @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 index to data of the cell, which is first layer below given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexBelowOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeight(int index); -+ -+ /** -+ * Returns the height of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightOriginal(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNswe(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNsweOriginal(int index); -+ -+ /** -+ * Sets the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @param nswe : New NSWE flag byte. -+ */ -+ public abstract void setNswe(int index, byte nswe); -+ -+ /** -+ * Saves the block in L2D format to {@link BufferedOutputStream}. Used only for L2D geodata conversion. -+ * @param stream : The stream. -+ * @throws IOException : Can't save the block to steam. -+ */ -+ public abstract void saveBlock(BufferedOutputStream stream) throws IOException; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (working copy) -@@ -0,0 +1,252 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockComplex(ByteBuffer bb, GeoFormat format) -+ { -+ // initialize buffer -+ _buffer = new byte[GeoStructure.BLOCK_CELLS * 3]; -+ -+ // load data -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // 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); -+ } -+ else -+ { -+ // get nswe -+ final byte nswe = bb.get(); -+ _buffer[i * 3] = nswe; -+ -+ // get height -+ final short height = bb.getShort(); -+ _buffer[(i * 3) + 1] = (byte) (height & 0x00FF); -+ _buffer[(i * 3) + 2] = (byte) (height >> 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height > worldZ ? height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height < worldZ ? height : Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public 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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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 int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_COMPLEX_L2D); -+ -+ // write block data -+ stream.write(_buffer, 0, GeoStructure.BLOCK_CELLS * 3); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (working copy) -@@ -0,0 +1,176 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockFlat extends ABlock -+{ -+ protected final short _height; -+ protected byte _nswe; -+ -+ /** -+ * Creates FlatBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockFlat(ByteBuffer bb, GeoFormat format) -+ { -+ _height = bb.getShort(); -+ _nswe = format != GeoFormat.L2D ? 0x0F : (byte) (0xFF); -+ if (format == GeoFormat.L2OFF) -+ { -+ bb.getShort(); -+ } -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return true; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height > worldZ ? _height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height < worldZ ? _height : Short.MAX_VALUE; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height > worldZ ? _nswe : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height < worldZ ? _nswe : 0; -+ } -+ -+ @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return index -+ return _height < worldZ ? 0 : -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _nswe = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_FLAT_L2D); -+ -+ // write height -+ stream.write((byte) (_height & 0x00FF)); -+ stream.write((byte) (_height >> 8)); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (working copy) -@@ -0,0 +1,465 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+import java.nio.ByteOrder; -+import java.util.Arrays; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockMultilayer extends ABlock -+{ -+ private static final int MAX_LAYERS = Byte.MAX_VALUE; -+ -+ private static ByteBuffer _temp; -+ -+ /** -+ * Initializes the temporarily buffer. -+ */ -+ public static void initialize() -+ { -+ // initialize temporarily buffer and sorting mechanism -+ _temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3); -+ _temp.order(ByteOrder.LITTLE_ENDIAN); -+ } -+ -+ /** -+ * Releases temporarily buffer. -+ */ -+ public static void release() -+ { -+ _temp = null; -+ } -+ -+ protected byte[] _buffer; -+ -+ /** -+ * Implicit constructor for children class. -+ */ -+ protected BlockMultilayer() -+ { -+ _buffer = null; -+ } -+ -+ /** -+ * Creates MultilayerBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockMultilayer(ByteBuffer bb, GeoFormat format) -+ { -+ // 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 = format != GeoFormat.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++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // get data -+ final short data = bb.getShort(); -+ -+ // add nswe and height -+ _temp.put((byte) (data & 0x000F)); -+ _temp.putShort((short) ((short) (data & 0xFFF0) >> 1)); -+ } -+ else -+ { -+ // add nswe -+ _temp.put(bb.get()); -+ -+ // add height -+ _temp.putShort(bb.getShort()); -+ } -+ } -+ } -+ -+ // 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 height -+ if (height > worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, return minimum value -+ return Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 height -+ if (height < worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, return maximum value -+ return Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 nswe -+ if (height > worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 nswe -+ if (height < worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @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 first layer data (first from bottom) -+ byte layers = _buffer[index++]; -+ -+ // loop though all cell layers, find closest layer -+ 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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ // get height -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(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]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ // get nswe -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ // set nswe -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_MULTILAYER_L2D); -+ -+ // for each cell -+ int index = 0; -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ // write layers count -+ final byte layers = _buffer[index++]; -+ stream.write(layers); -+ -+ // write cell data -+ stream.write(_buffer, index, layers * 3); -+ -+ // move index to next cell -+ index += layers * 3; -+ } -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (working copy) -@@ -0,0 +1,150 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockNull extends ABlock -+{ -+ private final byte _nswe; -+ -+ public BlockNull() -+ { -+ _nswe = (byte) 0xFF; -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return false; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweBelow(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) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) -+ { -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (nonexistent) -@@ -1,48 +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 . -- */ --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() -- { -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (nonexistent) -@@ -1,77 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (nonexistent) -@@ -1,56 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (working copy) -@@ -0,0 +1,39 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public enum GeoFormat -+{ -+ L2J("%d_%d.l2j"), -+ L2OFF("%d_%d_conv.dat"), -+ L2D("%d_%d.l2d"); -+ -+ private final String _filename; -+ -+ private GeoFormat(String filename) -+ { -+ _filename = filename; -+ } -+ -+ public String getFilename() -+ { -+ return _filename; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (working copy) -@@ -0,0 +1,67 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.geoengine.GeoEngine; -+import org.l2jmobius.gameserver.model.Location; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoLocation extends Location -+{ -+ private byte _nswe; -+ -+ public GeoLocation(int x, int y, int z) -+ { -+ super(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public void set(int x, int y, short z) -+ { -+ super.setXYZ(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public int getGeoX() -+ { -+ return _x; -+ } -+ -+ public int getGeoY() -+ { -+ return _y; -+ } -+ -+ @Override -+ public int getX() -+ { -+ return GeoEngine.getWorldX(_x); -+ } -+ -+ @Override -+ public int getY() -+ { -+ return GeoEngine.getWorldY(_y); -+ } -+ -+ public byte getNSWE() -+ { -+ return _nswe; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (working copy) -@@ -0,0 +1,70 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoStructure -+{ -+ // cells -+ public static final byte CELL_FLAG_E = 1 << 0; -+ public static final byte CELL_FLAG_W = 1 << 1; -+ public static final byte CELL_FLAG_S = 1 << 2; -+ public static final byte CELL_FLAG_N = 1 << 3; -+ public static final byte CELL_FLAG_SE = 1 << 4; -+ public static final byte CELL_FLAG_SW = 1 << 5; -+ public static final byte CELL_FLAG_NE = 1 << 6; -+ public static final byte CELL_FLAG_NW = (byte) (1 << 7); -+ -+ public static final int CELL_HEIGHT = 8; -+ public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6; -+ -+ // blocks -+ public static final byte TYPE_FLAT_L2J_L2OFF = 0; -+ public static final byte TYPE_FLAT_L2D = (byte) 0xD0; -+ public static final byte TYPE_COMPLEX_L2J = 1; -+ public static final byte TYPE_COMPLEX_L2OFF = 0x40; -+ public static final byte TYPE_COMPLEX_L2D = (byte) 0xD1; -+ 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) -+ public static final byte TYPE_MULTILAYER_L2D = (byte) 0xD2; -+ -+ 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; -+ -+ // regions -+ 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; -+ -+ // global geodata -+ private static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); -+ private 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; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (nonexistent) -@@ -1,42 +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 . -- */ --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); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (working copy) -@@ -0,0 +1,53 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public interface IGeoObject -+{ -+ /** -+ * Returns geodata X coordinate of the {@link IGeoObject}. -+ * @return int : Geodata X coordinate. -+ */ -+ int getGeoX(); -+ -+ /** -+ * Returns geodata Y coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Y coordinate. -+ */ -+ int getGeoY(); -+ -+ /** -+ * Returns geodata Z coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Z coordinate. -+ */ -+ int getGeoZ(); -+ -+ /** -+ * Returns height of the {@link IGeoObject}. -+ * @return int : Height. -+ */ -+ int getHeight(); -+ -+ /** -+ * Returns {@link IGeoObject} data. -+ * @return byte[][] : {@link IGeoObject} data. -+ */ -+ byte[][] getObjectGeoData(); -+} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (nonexistent) -@@ -1,47 +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 . -- */ --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(); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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); -- -- // 1 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (nonexistent) -@@ -1,55 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --/** -- * @author HorridoJoho -- */ --public final class NullRegion implements IRegion --{ -- public static final NullRegion INSTANCE = new NullRegion(); -- -- @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 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Region.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (nonexistent) -@@ -1,92 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (nonexistent) -@@ -1,87 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public abstract class AbstractNode --{ -- private T _loc; -- private AbstractNode _parent; -- -- public AbstractNode(T loc) -- { -- _loc = loc; -- } -- -- public void setParent(AbstractNode p) -- { -- _parent = p; -- } -- -- public AbstractNode getParent() -- { -- return _parent; -- } -- -- public T getLoc() -- { -- return _loc; -- } -- -- public void setLoc(T l) -- { -- _loc = l; -- } -- -- @Override -- public int hashCode() -- { -- final int prime = 31; -- int result = 1; -- result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); -- return result; -- } -- -- @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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (nonexistent) -@@ -1,33 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --/** -- * @author -Nemesiss- -- */ --public abstract class AbstractNodeLoc --{ -- public abstract int getX(); -- -- public abstract int getY(); -- -- public abstract int getZ(); -- -- public abstract int getNodeX(); -- -- public abstract int getNodeY(); --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (nonexistent) -@@ -1,67 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public class CellNode extends AbstractNode --{ -- 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (nonexistent) -@@ -1,348 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --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 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) -- { -- _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(); -- } -- -- public final List debugPath() -- { -- final List 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 void getNeighbors() -- { -- if (!_current.getLoc().canGoAll()) -- { -- 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); -- } -- -- // SouthEast -- if ((nodeE != null) && (nodeS != null)) -- { -- if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) -- { -- addNode(x + 1, y + 1, z, true); -- } -- } -- -- // SouthWest -- if ((nodeS != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) -- { -- addNode(x - 1, y + 1, z, true); -- } -- } -- -- // NorthEast -- if ((nodeN != null) && (nodeE != null)) -- { -- if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) -- { -- addNode(x + 1, y - 1, z, true); -- } -- } -- -- // NorthWest -- if ((nodeN != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) -- { -- addNode(x - 1, y - 1, z, true); -- } -- } -- } -- -- private 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(); -- // reinit node if needed -- if (result.getLoc() != null) -- { -- result.getLoc().set(x, y, z); -- } -- else -- { -- result.setLoc(new NodeLoc(x, y, z)); -- } -- } -- -- return result; -- } -- -- private 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 boolean isHighWeight(int x, int y, int z) -- { -- final CellNode result = getNode(x, y, z); -- if (result == null) -- { -- return true; -- } -- -- if (!result.getLoc().canGoAll()) -- { -- return true; -- } -- if (Math.abs(result.getLoc().getZ() - z) > 16) -- { -- return true; -- } -- -- return false; -- } -- -- private 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (working copy) -@@ -0,0 +1,87 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+ -+/** -+ * @author Hasha -+ */ -+public class Node -+{ -+ // node coords and nswe flag -+ private GeoLocation _loc; -+ -+ // node parent (for reverse path construction) -+ private Node _parent; -+ // node child (for moving over nodes during iteration) -+ private Node _child; -+ -+ // node G cost (movement cost = parent movement cost + current movement cost) -+ private double _cost = -1000; -+ -+ public void setLoc(int x, int y, int z) -+ { -+ _loc = new GeoLocation(x, y, z); -+ } -+ -+ public GeoLocation getLoc() -+ { -+ return _loc; -+ } -+ -+ public void setParent(Node parent) -+ { -+ _parent = parent; -+ } -+ -+ public Node getParent() -+ { -+ return _parent; -+ } -+ -+ public void setChild(Node child) -+ { -+ _child = child; -+ } -+ -+ public Node getChild() -+ { -+ return _child; -+ } -+ -+ public void setCost(double cost) -+ { -+ _cost = cost; -+ } -+ -+ public double getCost() -+ { -+ return _cost; -+ } -+ -+ public void free() -+ { -+ // reset node location -+ _loc = null; -+ -+ // reset node parent, child and cost -+ _parent = null; -+ _child = null; -+ _cost = -1000; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (working copy) -@@ -0,0 +1,306 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import java.util.concurrent.locks.ReentrantLock; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+ -+/** -+ * @author DS, Hasha; Credits to Diamond -+ */ -+public class NodeBuffer -+{ -+ private final ReentrantLock _lock = new ReentrantLock(); -+ private final int _size; -+ private final Node[][] _buffer; -+ -+ // center coordinates -+ private int _cx = 0; -+ private int _cy = 0; -+ -+ // target coordinates -+ private int _gtx = 0; -+ private int _gty = 0; -+ private short _gtz = 0; -+ -+ private Node _current = null; -+ -+ /** -+ * Constructor of NodeBuffer. -+ * @param size : one dimension size of buffer -+ */ -+ public NodeBuffer(int size) -+ { -+ // set size -+ _size = size; -+ -+ // initialize buffer -+ _buffer = new Node[size][size]; -+ for (int x = 0; x < size; x++) -+ { -+ for (int y = 0; y < size; y++) -+ { -+ _buffer[x][y] = 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 Node : first node of path -+ */ -+ public Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) -+ { -+ // set coordinates (middle of the line (gox,goy) - (gtx,gty), will be in the center of the buffer) -+ _cx = gox + ((gtx - gox - _size) / 2); -+ _cy = goy + ((gty - goy - _size) / 2); -+ -+ _gtx = gtx; -+ _gty = gty; -+ _gtz = gtz; -+ -+ _current = getNode(gox, goy, goz); -+ _current.setCost(getCostH(gox, goy, goz)); -+ -+ int count = 0; -+ do -+ { -+ // reached target? -+ if ((_current.getLoc().getGeoX() == _gtx) && (_current.getLoc().getGeoY() == _gty) && (Math.abs(_current.getLoc().getZ() - _gtz) < 8)) -+ { -+ return _current; -+ } -+ -+ // expand current node -+ expand(); -+ -+ // move pointer -+ _current = _current.getChild(); -+ } -+ while ((_current != null) && (count++ < Config.MAX_ITERATIONS)); -+ -+ return null; -+ } -+ -+ public boolean isLocked() -+ { -+ return _lock.tryLock(); -+ } -+ -+ public void free() -+ { -+ _current = null; -+ -+ for (Node[] nodes : _buffer) -+ { -+ for (Node node : nodes) -+ { -+ if (node.getLoc() != null) -+ { -+ node.free(); -+ } -+ } -+ } -+ -+ _lock.unlock(); -+ } -+ -+ /** -+ * Check _current Node and add its neighbors to the buffer. -+ */ -+ private final void expand() -+ { -+ // can't move anywhere, don't expand -+ final byte nswe = _current.getLoc().getNSWE(); -+ if (nswe == 0) -+ { -+ return; -+ } -+ -+ // get geo coords of the node to be expanded -+ final int x = _current.getLoc().getGeoX(); -+ final int y = _current.getLoc().getGeoY(); -+ final short z = (short) _current.getLoc().getZ(); -+ -+ // can move north, expand -+ if ((nswe & GeoStructure.CELL_FLAG_N) != 0) -+ { -+ addNode(x, y - 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move south, expand -+ if ((nswe & GeoStructure.CELL_FLAG_S) != 0) -+ { -+ addNode(x, y + 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_W) != 0) -+ { -+ addNode(x - 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_E) != 0) -+ { -+ addNode(x + 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move north-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NW) != 0) -+ { -+ addNode(x - 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move north-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NE) != 0) -+ { -+ addNode(x + 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SW) != 0) -+ { -+ addNode(x - 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SE) != 0) -+ { -+ addNode(x + 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ } -+ -+ /** -+ * Returns node, if it exists in buffer. -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param z : node Z coord -+ * @return Node : node, if exits in buffer -+ */ -+ private final Node getNode(int x, int y, short z) -+ { -+ // check node X out of coordinates -+ final int ix = x - _cx; -+ if ((ix < 0) || (ix >= _size)) -+ { -+ return null; -+ } -+ -+ // check node Y out of coordinates -+ final int iy = y - _cy; -+ if ((iy < 0) || (iy >= _size)) -+ { -+ return null; -+ } -+ -+ // get node -+ final Node result = _buffer[ix][iy]; -+ -+ // check and update -+ if (result.getLoc() == null) -+ { -+ result.setLoc(x, y, z); -+ } -+ -+ // return node -+ return result; -+ } -+ -+ /** -+ * Add node given by coordinates to the buffer. -+ * @param x : geo X coord -+ * @param y : geo Y coord -+ * @param z : geo Z coord -+ * @param weight : weight of movement to new node -+ */ -+ private final void addNode(int x, int y, short z, int weight) -+ { -+ // get node to be expanded -+ final Node node = getNode(x, y, z); -+ if (node == null) -+ { -+ return; -+ } -+ -+ // Z distance between nearby cells is higher than cell size -+ if (node.getLoc().getZ() > (z + (2 * GeoStructure.CELL_HEIGHT))) -+ { -+ return; -+ } -+ -+ // node was already expanded, return -+ if (node.getCost() >= 0) -+ { -+ return; -+ } -+ -+ node.setParent(_current); -+ if (node.getLoc().getNSWE() != (byte) 0xFF) -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + (weight * Config.OBSTACLE_MULTIPLIER)); -+ } -+ else -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + weight); -+ } -+ -+ Node current = _current; -+ int count = 0; -+ while ((current.getChild() != null) && (count < (Config.MAX_ITERATIONS * 4))) -+ { -+ count++; -+ if (current.getChild().getCost() > node.getCost()) -+ { -+ node.setChild(current.getChild()); -+ break; -+ } -+ current = current.getChild(); -+ } -+ -+ if (count >= (Config.MAX_ITERATIONS * 4)) -+ { -+ System.err.println("Pathfinding: too long loop detected, cost:" + node.getCost()); -+ } -+ -+ current.setChild(node); -+ } -+ -+ /** -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param i : node Z coord -+ * @return double : node cost -+ */ -+ private final double getCostH(int x, int y, int i) -+ { -+ final int dX = x - _gtx; -+ final int dY = y - _gty; -+ final int dZ = (i - _gtz) / GeoStructure.CELL_HEIGHT; -+ -+ // return (Math.abs(dX) + Math.abs(dY) + Math.abs(dZ)) * Config.HEURISTIC_WEIGHT; // Manhattan distance -+ return Math.sqrt((dX * dX) + (dY * dY) + (dZ * dZ)) * Config.HEURISTIC_WEIGHT; // Direct distance -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -- --/** -- * @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 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 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; -- // return super.hashCode(); -- } -- -- @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; -- if (_x != other._x) -- { -- return false; -- } -- if (_y != other._y) -- { -- return false; -- } -- if (_goNorth != other._goNorth) -- { -- return false; -- } -- if (_goEast != other._goEast) -- { -- return false; -- } -- if (_goSouth != other._goSouth) -- { -- return false; -- } -- if (_goWest != other._goWest) -- { -- return false; -- } -- if (_geoHeight != other._geoHeight) -- { -- return false; -- } -- return true; -- } --} -Index: java/org/l2jmobius/gameserver/model/actor/Creature.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/Creature.java (revision 8428) -+++ java/org/l2jmobius/gameserver/model/actor/Creature.java (working copy) -@@ -65,8 +65,6 @@ - import org.l2jmobius.gameserver.enums.TeleportWhereType; - import org.l2jmobius.gameserver.enums.UserInfoType; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; - import org.l2jmobius.gameserver.instancemanager.IdManager; - import org.l2jmobius.gameserver.instancemanager.MapRegionManager; - import org.l2jmobius.gameserver.instancemanager.QuestManager; -@@ -2566,7 +2564,7 @@ - - public boolean disregardingGeodata; - public int onGeodataPathIndex; -- public List geoPath; -+ public List geoPath; - public int geoPathAccurateTx; - public int geoPathAccurateTy; - public int geoPathGtx; -@@ -3443,7 +3441,7 @@ - if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) - { - // Path calculation -- overrides previous movement check -- m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); -+ m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); - if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found - { - if (isPlayer() && !_isFlying && !isInWater) -Index: java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (revision 8424) -+++ java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (working copy) -@@ -12746,7 +12746,7 @@ - { - if (Config.CORRECT_PLAYER_Z) - { -- final int nearestZ = GeoEngine.getInstance().getHigherHeight(getX(), getY(), getZ()); -+ final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); - if (getZ() < nearestZ) - { - teleToLocation(new Location(getX(), getY(), nearestZ)); -Index: java/org/l2jmobius/gameserver/util/GeoUtils.java -=================================================================== ---- java/org/l2jmobius/gameserver/util/GeoUtils.java (revision 8409) -+++ java/org/l2jmobius/gameserver/util/GeoUtils.java (working copy) -@@ -19,7 +19,7 @@ - import java.awt.Color; - - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; - import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; - -@@ -26,25 +26,25 @@ - /** - * @author HorridoJoho - */ --public final class GeoUtils -+public class GeoUtils - { - public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); - - final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); - - while (iter.next()) - { -- final int wx = GeoEngine.getInstance().getWorldX(iter.x()); -- final int wy = GeoEngine.getInstance().getWorldY(iter.y()); -+ final int wx = GeoEngine.getWorldX(iter.x()); -+ final int wy = GeoEngine.getWorldY(iter.y()); - - prim.addPoint(Color.RED, wx, wy, z); - } -@@ -53,21 +53,21 @@ - - public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); -- int wy = GeoEngine.getInstance().getWorldY(prevY); -+ int wx = GeoEngine.getWorldX(prevX); -+ int wy = GeoEngine.getWorldY(prevY); - int wz = iter.z(); - prim.addPoint(Color.RED, wx, wy, wz); - -@@ -78,8 +78,8 @@ - - if ((curX != prevX) || (curY != prevY)) - { -- wx = GeoEngine.getInstance().getWorldX(curX); -- wy = GeoEngine.getInstance().getWorldY(curY); -+ wx = GeoEngine.getWorldX(curX); -+ wy = GeoEngine.getWorldY(curY); - wz = iter.z(); - - prim.addPoint(Color.RED, wx, wy, wz); -@@ -93,7 +93,7 @@ - - private static Color getDirectionColor(int x, int y, int z, int nswe) - { -- if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) -+ if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) - { - return Color.GREEN; - } -@@ -109,9 +109,8 @@ - int iPacket = 0; - - ExServerPrimitive exsp = null; -- final GeoEngine ge = GeoEngine.getInstance(); -- final int playerGx = ge.getGeoX(player.getX()); -- final int playerGy = ge.getGeoY(player.getY()); -+ final int playerGx = GeoEngine.getGeoX(player.getX()); -+ final int playerGy = GeoEngine.getGeoY(player.getY()); - for (int dx = -geoRadius; dx <= geoRadius; ++dx) - { - for (int dy = -geoRadius; dy <= geoRadius; ++dy) -@@ -135,12 +134,12 @@ - final int gx = playerGx + dx; - final int gy = playerGy + dy; - -- final int x = ge.getWorldX(gx); -- final int y = ge.getWorldY(gy); -- final int z = ge.getNearestZ(gx, gy, player.getZ()); -+ final int x = GeoEngine.getWorldX(gx); -+ final int y = GeoEngine.getWorldY(gy); -+ final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); - - // north arrow -- Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); -+ Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); - 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); -@@ -147,7 +146,7 @@ - exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z); - - // east arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); - 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); -@@ -154,13 +153,13 @@ - exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z); - - // south arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); - 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, Cell.NSWE_WEST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); - 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); -@@ -188,15 +187,15 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_EAST; -+ return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_EAST; -+ return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; - } - else - { -- return Cell.NSWE_EAST; -+ return GeoStructure.CELL_FLAG_E; // Direction.EAST; - } - } - else if (x < lastX) // west -@@ -203,26 +202,27 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_WEST; -+ return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_WEST; -+ return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; - } - else - { -- return Cell.NSWE_WEST; -+ return GeoStructure.CELL_FLAG_W; // Direction.WEST; - } - } -- else // unchanged x -+ else -+ // unchanged x - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH; -+ return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH; -+ return GeoStructure.CELL_FLAG_N; // Direction.NORTH; - } - else - { -Index: java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java -=================================================================== ---- java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (nonexistent) -+++ java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (working copy) -@@ -0,0 +1,386 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.tools.geodataconverter; -+ -+import java.io.BufferedOutputStream; -+import java.io.File; -+import java.io.FileOutputStream; -+import java.io.RandomAccessFile; -+import java.nio.ByteOrder; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.Scanner; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.commons.util.PropertiesParser; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoDataConverter -+{ -+ private static GeoFormat _format; -+ private static ABlock[][] _blocks; -+ -+ public static void main(String[] args) -+ { -+ // initialize config -+ loadGeoengineConfigs(); -+ -+ // get geodata format -+ String type = ""; -+ try (Scanner scn = new Scanner(System.in)) -+ { -+ while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) -+ { -+ System.out.println("GeoDataConverter: Select source geodata type:"); -+ System.out.println(" J: L2J (e.g. 23_22.l2j)"); -+ System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); -+ System.out.println(" E: Exit"); -+ System.out.print("Choice: "); -+ type = scn.next(); -+ } -+ } -+ if (type.equalsIgnoreCase("E")) -+ { -+ System.exit(0); -+ } -+ -+ _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; -+ -+ // start conversion -+ System.out.println("GeoDataConverter: Converting all " + _format + " files."); -+ -+ // initialize geodata container -+ _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files -+ int converted = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) -+ { -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) -+ { -+ final String input = String.format(_format.getFilename(), rx, ry); -+ final String filepath = Config.GEODATA_PATH; -+ final File f = new File(filepath + input); -+ if (f.exists() && !f.isDirectory()) -+ { -+ // load geodata -+ if (!loadGeoBlocks(input)) -+ { -+ System.out.println("GeoDataConverter: Unable to load " + input + " region file."); -+ continue; -+ } -+ -+ // recalculate nswe -+ if (!recalculateNswe()) -+ { -+ System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); -+ continue; -+ } -+ -+ // save geodata -+ final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); -+ if (!saveGeoBlocks(output)) -+ { -+ System.out.println("GeoDataConverter: Unable to save " + output + " region file."); -+ continue; -+ } -+ -+ converted++; -+ System.out.println("GeoDataConverter: Created " + output + " region file."); -+ } -+ } -+ } -+ System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); -+ } -+ -+ /** -+ * Loads geo blocks from buffer of the region file. -+ * @param filename : The name of the to load. -+ * @return boolean : True when successful. -+ */ -+ private static boolean loadGeoBlocks(String filename) -+ { -+ // region file is load-able, try to load it -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) -+ { -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) -+ if (_format == GeoFormat.L2OFF) -+ { -+ for (int i = 0; i < 18; i++) -+ { -+ buffer.get(); -+ } -+ } -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ if (_format == GeoFormat.L2J) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2J: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2J: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ else -+ { -+ // get block type -+ final short type = buffer.getShort(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ default: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ if (buffer.remaining() > 0) -+ { -+ System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ return false; -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); -+ return false; -+ } -+ } -+ -+ /** -+ * Recalculate diagonal flags for the region file. -+ * @return boolean : True when successful. -+ */ -+ private static boolean recalculateNswe() -+ { -+ try -+ { -+ for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) -+ { -+ for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) -+ { -+ // get block -+ final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // skip flat blocks -+ if (block instanceof BlockFlat) -+ { -+ continue; -+ } -+ -+ // for complex and multilayer blocks go though all layers -+ short height = Short.MAX_VALUE; -+ int index; -+ while ((index = block.getIndexBelow(x, y, height)) != -1) -+ { -+ // get height and nswe -+ height = block.getHeight(index); -+ byte nswe = block.getNswe(index); -+ -+ // update nswe with diagonal flags -+ nswe = updateNsweBelow(x, y, height, nswe); -+ -+ // set nswe of the cell -+ block.setNswe(index, nswe); -+ } -+ } -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ /** -+ * Updates the NSWE flag with diagonal flags. -+ * @param x : Geodata X coordinate. -+ * @param y : Geodata Y coordinate. -+ * @param z : Geodata Z coordinate. -+ * @param nsweValue : NSWE flag to be updated. -+ * @return byte : Updated NSWE flag. -+ */ -+ private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) -+ { -+ byte nswe = nsweValue; -+ -+ // calculate virtual layer height -+ final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); -+ -+ // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) -+ final byte nsweN = getNsweBelow(x, y - 1, height); -+ final byte nsweS = getNsweBelow(x, y + 1, height); -+ final byte nsweW = getNsweBelow(x - 1, y, height); -+ final byte nsweE = getNsweBelow(x + 1, y, height); -+ -+ // north-west -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NW; -+ } -+ -+ // north-east -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NE; -+ } -+ -+ // south-west -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SW; -+ } -+ -+ // south-east -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SE; -+ } -+ -+ return nswe; -+ } -+ -+ private static byte getNsweBelow(int geoX, int geoY, short worldZ) -+ { -+ // out of geo coordinates -+ if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) -+ { -+ return 0; -+ } -+ -+ // out of geo coordinates -+ if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) -+ { -+ return 0; -+ } -+ -+ // get block -+ final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // get index, when valid, return nswe -+ final int index = block.getIndexBelow(geoX, geoY, worldZ); -+ return index == -1 ? 0 : block.getNswe(index); -+ } -+ -+ /** -+ * Save region file to file. -+ * @param filename : The name of file to save. -+ * @return boolean : True when successful. -+ */ -+ private static boolean saveGeoBlocks(String filename) -+ { -+ try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) -+ { -+ // loop over region blocks and save each block -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[ix][iy].saveBlock(bos); -+ } -+ } -+ -+ // flush data to file -+ bos.flush(); -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ private static void loadGeoengineConfigs() -+ { -+ final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); -+ Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); -+ Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); -+ Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); -+ Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); -+ Config.PATHFINDING = geoData.getBoolean("PathFinding", true); -+ Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -+ Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); -+ Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); -+ Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); -+ Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); -+ Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); -+ } -+} -\ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/geodata/Readme.txt b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/geodata/Readme.txt index a480c8c380..2611882e56 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/geodata/Readme.txt +++ b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/geodata/Readme.txt @@ -23,8 +23,8 @@ a - Prerequisites b - Make it work ---------------------------------------------- -To make geodata working: -* unpack your geodata files into "/data/geodata" folder -* open "/config/GeoEngine.ini" with your favorite text editor and then edit following config: - - CoordSynchronize = 2 -* If you do not use any geodata files, the server will automatically change this setting to -1. +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. diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/ai/others/FleeMonsters.java b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/ai/others/FleeMonsters.java index 4562336103..a971742033 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/ai/others/FleeMonsters.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/ai/others/FleeMonsters.java @@ -59,7 +59,7 @@ public class FleeMonsters extends AbstractNpcAI final int posX = (int) (npc.getX() + (FLEE_DISTANCE * Math.cos(radians))); final int posY = (int) (npc.getY() + (FLEE_DISTANCE * Math.sin(radians))); final int posZ = npc.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, attacker.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); return super.onAttack(npc, attacker, damage, isSummon); } diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/ai/others/RandomWalkingGuards.java b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/ai/others/RandomWalkingGuards.java index b99becfdc6..780502d5d6 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/ai/others/RandomWalkingGuards.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/ai/others/RandomWalkingGuards.java @@ -55,7 +55,7 @@ public class RandomWalkingGuards extends AbstractNpcAI if (!npc.isInCombat()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, Config.MAX_DRIFT_RANGE); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("RANDOM_WALK", getRandom(MIN_WALK_DELAY, MAX_WALK_DELAY), npc, null); } diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index 524652cbc1..18511c6541 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { @@ -133,8 +133,8 @@ public class AdminGeodata implements IAdminCommandHandler } case "admin_geomap": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y + " (" + ((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + "," + ((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + " to " + ((((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + "," + ((((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + ")"); break; } diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java index f3dffa1227..76572f7f25 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java @@ -57,8 +57,8 @@ public class AdminMissingHtmls implements IAdminCommandHandler { case "admin_geomap_missing_htmls": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final int topLeftX = (x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE; final int topLeftY = (y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE; final int bottomRightX = (((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1; diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index fdb2319138..a0a34f1051 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -19,9 +19,9 @@ package handlers.admincommandhandlers; import java.util.List; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; +import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.util.BuilderUtil; @@ -44,13 +44,13 @@ public class AdminPathNode implements IAdminCommandHandler } if (activeChar.getTarget() != null) { - final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { BuilderUtil.sendSysMessage(activeChar, "No Route!"); return true; } - for (AbstractNodeLoc a : path) + for (Location a : path) { BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/Blink.java b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/Blink.java index 22f2c84eae..22697e8e20 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/Blink.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/Blink.java @@ -88,7 +88,7 @@ public class Blink extends AbstractEffect final int y = effected.getY() + y1; final int z = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, destination, _flyType, _flySpeed, _flyDelay, _animationSpeed)); diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/Fear.java b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/Fear.java index 95a5afdf98..88c795e04b 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/Fear.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/Fear.java @@ -100,7 +100,7 @@ public class Fear extends AbstractEffect final int posY = (int) (effected.getY() + (FEAR_RANGE * Math.sin(radians))); final int posZ = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); } } diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java index fddd94c60a..55c2e78d8a 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java @@ -57,7 +57,7 @@ public class FlyAway extends AbstractEffect final int y = (int) (effector.getY() - (nRadius * (dy / distance))); final int z = effector.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.broadcastPacket(new FlyToLocation(effected, destination, FlyType.THROW_UP)); effected.setXYZ(destination); diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java index 1fbd8239b0..89ad8670dc 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java @@ -133,7 +133,7 @@ public class KnockBack extends AbstractEffect final int x = (int) (effected.getX() + (_distance * Math.cos(radians))); final int y = (int) (effected.getY() + (_distance * Math.sin(radians))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, loc, _type, _speed, _delay, _animationSpeed)); diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/PullBack.java b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/PullBack.java index ed6a37eff6..38fe2fe9d0 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/PullBack.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/PullBack.java @@ -73,7 +73,7 @@ public class PullBack extends AbstractEffect } // In retail, you get debuff, but you are not even moved if there is obstacle. You are still disabled from using skills and moving though. - if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effector.getInstanceWorld())) + if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effected.getInstanceWorld())) { effected.broadcastPacket(new FlyToLocation(effected, effector, _type, _speed, _delay, _animationSpeed)); effected.setXYZ(effector.getX(), effector.getY(), GeoEngine.getInstance().getHeight(effector.getX(), effector.getY(), effector.getZ()) + 10); diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java index d6f9664141..813e496c47 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java @@ -87,7 +87,7 @@ public class TeleportToSummon extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = summon.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java index d3c263978c..c9dc34ac22 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java @@ -76,7 +76,7 @@ public class TeleportToTarget extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java index f2148adb54..c7ca6f64cb 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java @@ -65,7 +65,7 @@ public class RollingDice implements IItemHandler final int x = player.getX() + x1; final int y = player.getY() + y1; final int z = player.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); Broadcast.toSelfAndKnownPlayers(player, new Dice(player.getObjectId(), itemId, number, destination.getX(), destination.getY(), destination.getZ())); final SystemMessage sm = new SystemMessage(SystemMessageId.C1_HAS_ROLLED_A_S2); diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/targethandlers/Ground.java b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/targethandlers/Ground.java index 82ab321c29..d8c73627f5 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/targethandlers/Ground.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/dist/game/data/scripts/handlers/targethandlers/Ground.java @@ -52,7 +52,7 @@ public class Ground implements ITargetTypeHandler return null; } - if (!GeoEngine.getInstance().canSeeTarget(creature, worldPosition)) + if (!GeoEngine.getInstance().canSeeLocation(creature, worldPosition)) { if (sendMessage) { diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/Config.java index 297b3a4ccf..a94687f70d 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/Config.java @@ -61,6 +61,7 @@ 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; @@ -893,12 +894,17 @@ public class Config // GeoEngine // -------------------------------------------------- public static Path GEODATA_PATH; + public static GeoType GEODATA_TYPE; public static boolean PATHFINDING; public static String PATHFIND_BUFFERS; - public static float LOW_WEIGHT; - public static float MEDIUM_WEIGHT; - public static float HIGH_WEIGHT; - public static float DIAGONAL_WEIGHT; + public static int MOVE_WEIGHT; + public static int MOVE_WEIGHT_DIAG; + public static int OBSTACLE_WEIGHT; + 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; /** Attribute System */ public static int S_WEAPON_STONE; @@ -2393,12 +2399,17 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); + GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); - MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); - HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); - DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); + MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); + OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); + HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); + MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); + MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); // Load AllowedPlayerRaces config file (if exists) final PropertiesParser AllowedPlayerRaces = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE); diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/commons/util/CommonUtil.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/commons/util/CommonUtil.java index 29a88cf20c..86fa98ff10 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/commons/util/CommonUtil.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/commons/util/CommonUtil.java @@ -591,6 +591,17 @@ public class CommonUtil return formatter.format(value); } + /** + * @param numToTest : The number to test. + * @param min : The minimum limit. + * @param max : The maximum limit. + * @return the number or one of the limit (mininum / maximum). + */ + public static int limit(int numToTest, int min, int max) + { + return (numToTest > max) ? max : ((numToTest < min) ? min : numToTest); + } + public static boolean isNullOrEmpty(CharSequence value) { return isNull(value) || (value.length() == 0); diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/commons/util/Point2D.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/commons/util/Point2D.java new file mode 100644 index 0000000000..ebb6272e5e --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/commons/util/Point2D.java @@ -0,0 +1,180 @@ +/* + * 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 . + */ +package org.l2jmobius.commons.util; + +import java.util.Objects; + +/** + * A datatype used to retain a 2D (x/y) point. It got the capability to be set and cleaned. + */ +public class Point2D +{ + protected volatile int _x; + protected volatile int _y; + + public Point2D(int x, int y) + { + _x = x; + _y = y; + } + + @Override + public Point2D clone() + { + return new Point2D(_x, _y); + } + + @Override + public String toString() + { + return _x + ", " + _y; + } + + @Override + public int hashCode() + { + return Objects.hash(_x, _y); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final Point2D other = (Point2D) obj; + return (_x == other._x) && (_y == other._y); + } + + /** + * @param x : The X coord to test. + * @param y : The Y coord to test. + * @return True if all coordinates equals this {@link Point2D} coordinates. + */ + public boolean equals(int x, int y) + { + return (_x == x) && (_y == y); + } + + public int getX() + { + return _x; + } + + public void setX(int x) + { + _x = x; + } + + public int getY() + { + return _y; + } + + public void setY(int y) + { + _y = y; + } + + public void set(int x, int y) + { + _x = x; + _y = y; + } + + /** + * Refresh the current {@link Point2D} using a reference {@link Point2D} and a distance. The new destination is calculated to go in opposite side of the {@link Point2D} reference.
+ *
+ * This method is perfect to calculate fleeing characters position. + * @param referenceLoc : The Point2D used as position. + * @param distance : The distance to be set between current and new position. + */ + public void setFleeing(Point2D referenceLoc, int distance) + { + final double xDiff = referenceLoc.getX() - _x; + final double yDiff = referenceLoc.getY() - _y; + + final double yxRation = Math.abs(xDiff / yDiff); + + final int y = (int) (distance / (yxRation + 1)); + final int x = (int) (y * yxRation); + + _x += (xDiff < 0 ? x : -x); + _y += (yDiff < 0 ? y : -y); + } + + public void clean() + { + _x = 0; + _y = 0; + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @return The distance between this {@Point2D} and some given coordinates. + */ + public double distance2D(int x, int y) + { + final double dx = (double) _x - x; + final double dy = (double) _y - y; + + return Math.sqrt((dx * dx) + (dy * dy)); + } + + /** + * @param point : The {@link Point2D} to test. + * @return The distance between this {@Point2D} and the {@link Point2D} set as parameter. + */ + public double distance2D(Point2D point) + { + return distance2D(point.getX(), point.getY()); + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of some given coordinates. + */ + public boolean isIn2DRadius(int x, int y, int radius) + { + return distance2D(x, y) < radius; + } + + /** + * @param point : The Point2D to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of the {@link Point2D} set as parameter. + */ + public boolean isIn2DRadius(Point2D point, int radius) + { + return distance2D(point) < radius; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/AttackableAI.java index 547963e3e8..dc29d4cf6e 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/AttackableAI.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/ai/AttackableAI.java @@ -578,7 +578,7 @@ public class AttackableAI extends CreatureAI } // Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast) - final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); + final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); moveTo(moveLoc.getX(), moveLoc.getY(), moveLoc.getZ()); } } @@ -801,7 +801,7 @@ public class AttackableAI extends CreatureAI final int newZ = npc.getZ() + 30; // Mobius: Verify destination. Prevents wall collision issues and fixes monsters not avoiding obstacles. - moveTo(GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); + moveTo(GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); } return; } diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/data/SpawnTable.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/data/SpawnTable.java index a81201f69e..19f0db5b3e 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/data/SpawnTable.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/data/SpawnTable.java @@ -114,8 +114,8 @@ public class SpawnTable } // XML file for spawn - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); // Write info to XML @@ -192,8 +192,8 @@ public class SpawnTable if (update) { - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = spawn.getNpcSpawnTemplate() != null ? spawn.getNpcSpawnTemplate().getSpawnTemplate().getFile() : new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); final File tempFile = new File(spawnFile.getAbsolutePath().substring(Config.DATAPACK_ROOT.getAbsolutePath().length() + 1).replace('\\', '/') + ".tmp"); try diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/enums/GeoType.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/enums/GeoType.java new file mode 100644 index 0000000000..f77aa5eac4 --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/enums/GeoType.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.enums; + +public enum GeoType +{ + L2J("%d_%d.l2j"), + L2OFF("%d_%d_conv.dat"); + + private final String _filename; + + private GeoType(String filename) + { + _filename = filename; + } + + public String getFilename() + { + return _filename; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java new file mode 100644 index 0000000000..e62f50a8df --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java @@ -0,0 +1,144 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 9d255a5f54..7bef9c770b 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,72 +16,63 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.channels.FileChannel.MapMode; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.logging.Level; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; import java.util.logging.Logger; import org.l2jmobius.Config; +import org.l2jmobius.commons.util.CommonUtil; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; -import org.l2jmobius.gameserver.geoengine.geodata.IRegion; -import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; -import org.l2jmobius.gameserver.geoengine.geodata.Region; +import org.l2jmobius.gameserver.enums.GeoType; +import org.l2jmobius.gameserver.enums.MoveDirectionType; +import org.l2jmobius.gameserver.geoengine.geodata.ABlock; +import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; +import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; +import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; +import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.model.interfaces.ILocational; -import org.l2jmobius.gameserver.util.GeoUtils; -import org.l2jmobius.gameserver.util.LinePointIterator; -import org.l2jmobius.gameserver.util.LinePointIterator3D; -/** - * @author -Nemesiss-, HorridoJoho - */ public class GeoEngine { - private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - - private static final int WORLD_MIN_X = -655360; - private static final int WORLD_MIN_Y = -589824; - private static final int WORLD_MIN_Z = -16384; - - /** 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; - - /** The regions array */ - private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; - private static final int ELEVATED_SEE_OVER_DISTANCE = 2; - private static final int MAX_SEE_OVER_HEIGHT = 48; - protected GeoEngine() + private final ABlock[][] _blocks; + private final BlockNull _nullBlock; + + // Pre-allocated buffers. + private final BufferHolder[] _buffers; + + public GeoEngine() { LOGGER.info("GeoEngine: Initializing..."); - for (int i = 0; i < _regions.length(); i++) - { - _regions.set(i, NullRegion.INSTANCE); - } + // Initialize block container. + _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; + + // Load null block. + _nullBlock = new BlockNull(); + + // Initialize multilayer temporarily buffer. + BlockMultilayer.initialize(); + + // Load geo files according to geoengine config setup. int loaded = 0; try { @@ -92,23 +83,52 @@ public class GeoEngine final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); if (Files.exists(geoFilePath)) { - try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + // Region file is load-able, try to load it. + if (loadGeoBlocks(regionX, regionY)) { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); loaded++; } } + else + { + // Region file is not load-able, load null blocks. + loadNullBlocks(regionX, regionY); + } } } } catch (Exception e) { - LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + LOGGER.warning("GeoEngine: Failed to load geodata! " + e); System.exit(1); } - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + // Release multilayer block temporarily buffer. + BlockMultilayer.release(); + + String[] array = Config.PATHFIND_BUFFERS.split(";"); + _buffers = new BufferHolder[array.length]; + + int count = 0; + for (int i = 0; i < array.length; i++) + { + String buf = array[i]; + String[] args = buf.split("x"); + + try + { + int size = Integer.parseInt(args[1]); + count += size; + _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); + } + catch (Exception e) + { + LOGGER.warning("Could not load buffer setting:" + buf + ". " + e); + } + } + LOGGER.info("Loaded " + count + " node buffers."); + // Avoid wrong configs when no files are loaded. if ((loaded == 0) && Config.PATHFINDING) { @@ -118,616 +138,913 @@ public class GeoEngine } /** - * @param geoX - * @param geoY - * @return the region + * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer and log this situation. + * @param size : pre-calculated minimal required size + * @return NodeBuffer : buffer */ - private IRegion getRegion(int geoX, int geoY) + private NodeBuffer getBuffer(int size) { - final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); - if ((region < 0) || (region >= _regions.length())) + NodeBuffer current = null; + for (BufferHolder holder : _buffers) { - return null; - } - return _regions.get(region); - } - - /** - * @param filePath - * @param regionX - * @param regionY - * @throws IOException - */ - 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()).load().order(ByteOrder.LITTLE_ENDIAN))); - } - } - - /** - * @param regionX - * @param regionY - */ - public void unloadRegion(int regionX, int regionY) - { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); - } - - /** - * @param geoX - * @param geoY - * @return if geodata exist - */ - public boolean hasGeoPos(int geoX, int geoY) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return false; - } - return region.hasGeo(); - } - - /** - * Checks the specified position for available geodata. - * @param x the world x - * @param y the world y - * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise - */ - public boolean hasGeo(int x, int y) - { - return hasGeoPos(getGeoX(x), getGeoY(y)); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe check - */ - public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return true; - } - return region.checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe anti-corner cut - */ - public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) - { - boolean can = true; - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - return can && checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the nearest Z value - */ - public int getNearestZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNearestZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next lower Z value - */ - public int getNextLowerZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextLowerZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next higher Z value - */ - public int getNextHigherZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextHigherZ(geoX, geoY, worldZ); - } - - /** - * Gets the Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHeight(int x, int y, int z) - { - return getNearestZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next lower Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getLowerHeight(int x, int y, int z) - { - return getNextLowerZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next higher Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHigherHeight(int x, int y, int z) - { - return getNextHigherZ(getGeoX(x), getGeoY(y), z); - } - - /** - * @param worldX - * @return the geo X - */ - public int getGeoX(int worldX) - { - return (worldX - WORLD_MIN_X) / 16; - } - - /** - * @param worldY - * @return the geo Y - */ - public int getGeoY(int worldY) - { - return (worldY - WORLD_MIN_Y) / 16; - } - - /** - * @param worldZ - * @return the geo Z - */ - public int getGeoZ(int worldZ) - { - return (worldZ - WORLD_MIN_Z) / 16; - } - - /** - * @param geoX - * @return the world X - */ - public int getWorldX(int geoX) - { - return (geoX * 16) + WORLD_MIN_X + 8; - } - - /** - * @param geoY - * @return the world Y - */ - public int getWorldY(int geoY) - { - return (geoY * 16) + WORLD_MIN_Y + 8; - } - - /** - * @param geoZ - * @return the world Z - */ - public int getWorldZ(int geoZ) - { - return (geoZ * 16) + WORLD_MIN_Z + 8; - } - - /** - * Can see target. Doors as target always return true. Checks doors between. - * @param cha the character - * @param target the target - * @return {@code true} if the character can see the target (LOS), {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, WorldObject target) - { - if (target.isDoor()) - { - // Can always see doors. - return true; - } - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); - } - - /** - * Can see target. Checks doors between. - * @param cha the character - * @param worldPosition the world position - * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) - { - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param world - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @param tworld the target's instanceId - * @return - */ - public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) - { - return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instance - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) - { - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) - { - return false; - } - return canSeeTarget(x, y, z, tx, ty, tz); - } - - /** - * @param prevX - * @param prevY - * @param prevGeoZ - * @param curX - * @param curY - * @param nswe - * @return the LOS Z value - */ - private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) - { - if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) - { - throw new RuntimeException("Multiple directions!"); - } - - if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) - { - return getNearestZ(curX, curY, prevGeoZ); - } - - return getNextHigherZ(curX, curY, prevGeoZ); - } - - /** - * Can see target. Does not check doors between. - * @param xValue the x coordinate - * @param yValue the y coordinate - * @param zValue the z coordinate - * @param txValue the target's x coordinate - * @param tyValue the target's y coordinate - * @param tzValue the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) - { - int x = xValue; - int y = yValue; - int tx = txValue; - int ty = tyValue; - - int geoX = getGeoX(x); - int geoY = getGeoY(y); - int tGeoX = getGeoX(tx); - int tGeoY = getGeoY(ty); - - int z = getNearestZ(geoX, geoY, zValue); - int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - // Fastpath. - if ((geoX == tGeoX) && (geoY == tGeoY)) - { - if (hasGeoPos(tGeoX, tGeoY)) - { - return z == tz; - } - return true; - } - - if (tz > z) - { - int tmp = tx; - tx = x; - x = tmp; - - tmp = ty; - ty = y; - y = tmp; - - tmp = tz; - tz = z; - z = tmp; - - tmp = tGeoX; - tGeoX = geoX; - geoX = tmp; - - tmp = tGeoY; - tGeoY = geoY; - geoY = tmp; - } - - final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); - // First point is guaranteed to be available, skip it, we can always see our own position. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - final int prevZ = pointIter.z(); - int prevGeoZ = prevZ; - int ptIndex = 0; - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - - if ((curX == prevX) && (curY == prevY)) + // Find proper size of buffer. + if (holder._size < size) { continue; } - final int beeCurZ = pointIter.z(); - int curGeoZ = prevGeoZ; - - // Check if the position has geodata. - if (hasGeoPos(curX, curY)) + // Find unlocked NodeBuffer. + for (NodeBuffer buffer : holder._buffer) { - final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); - curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); - int maxHeight; - if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) + if (!buffer.isLocked()) { - maxHeight = z + MAX_SEE_OVER_HEIGHT; - } - else - { - maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; + continue; } - boolean canSeeThrough = false; - if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) + return buffer; + } + + LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); + + // NodeBuffer not found, allocate temporary buffer. + current = new NodeBuffer(holder._size); + current.isLocked(); + } + + return current; + } + + /** + * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + * @return boolean : True, when geodata file was loaded without problem. + */ + private boolean loadGeoBlocks(int regionX, int regionY) + { + final String filename = String.format(Config.GEODATA_TYPE.getFilename(), regionX, regionY); + final String filepath = Config.GEODATA_PATH + "\\" + filename; + + // Standard load. + try (RandomAccessFile raf = new RandomAccessFile(filepath, "r"); + FileChannel fc = raf.getChannel()) + { + // Initialize file buffer. + MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // Load 18B header for L2OFF geodata (1st and 2nd byte...region X and Y). + if (Config.GEODATA_TYPE == GeoType.L2OFF) + { + for (int i = 0; i < 18; i++) { - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) + buffer.get(); + } + } + + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Loop over region blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + if (Config.GEODATA_TYPE == GeoType.L2J) { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) - { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); + // Get block type. + final byte type = buffer.get(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + case GeoStructure.TYPE_MULTILAYER_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + default: + { + throw new IllegalArgumentException("Unknown block type: " + type); + } + } } else { - canSeeThrough = true; + // Get block type. + final short type = buffer.getShort(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + default: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + } } } + } + + // Check data consistency. + if (buffer.remaining() > 0) + { + LOGGER.warning("Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); + } + + // Loading was successful. + return true; + } + catch (Exception e) + { + // An error occured while loading, load null blocks. + LOGGER.warning("Error loading " + filename + " region file. " + e); + + // Replace whole region file with null blocks. + loadNullBlocks(regionX, regionY); + + // Loading was not successful. + return false; + } + } + + /** + * Loads null blocks. Used when no region file is detected or an error occurs during loading. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + */ + private void loadNullBlocks(int regionX, int regionY) + { + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Load all null blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + _blocks[blockX + ix][blockY + iy] = _nullBlock; + } + } + } + + /** + * Converts world X to geodata X. + * @param worldX + * @return int : Geo X + */ + public static int getGeoX(int worldX) + { + return (worldX - World.WORLD_X_MIN) >> 4; + } + + /** + * Converts world Y to geodata Y. + * @param worldY + * @return int : Geo Y + */ + public static int getGeoY(int worldY) + { + return (worldY - World.WORLD_Y_MIN) >> 4; + } + + /** + * Converts geodata X to world X. + * @param geoX + * @return int : World X + */ + public static int getWorldX(int geoX) + { + return (geoX << 4) + World.WORLD_X_MIN + 8; + } + + /** + * Converts geodata Y to world Y. + * @param geoY + * @return int : World Y + */ + public static int getWorldY(int geoY) + { + return (geoY << 4) + World.WORLD_Y_MIN + 8; + } + + /** + * Returns block of geodata on given coordinates. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return {@link ABlock} : Block of geodata. + */ + public ABlock getBlock(int geoX, int geoY) + { + final int x = geoX / GeoStructure.BLOCK_CELLS_X; + if ((x < 0) || (x >= GeoStructure.GEO_BLOCKS_X)) + { + return null; + } + final int y = geoY / GeoStructure.BLOCK_CELLS_Y; + if ((y < 0) || (y >= GeoStructure.GEO_BLOCKS_Y)) + { + return null; + } + return _blocks[x][y]; + } + + /** + * Check if geo coordinates has geo. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return boolean : True, if given geo coordinates have geodata + */ + public boolean hasGeoPos(int geoX, int geoY) + { + final ABlock block = getBlock(geoX, geoY); + return (block != null) && block.hasGeoPos(); + } + + /** + * Returns the height of cell, which is closest to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell geodata Z coordinate, closest to given coordinates. + */ + public short getHeightNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return (short) worldZ; + } + return block.getHeightNearest(geoX, geoY, worldZ); + } + + /** + * Returns the NSWE flag byte of cell, which is closes to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. + */ + public byte getNsweNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return GeoStructure.CELL_FLAG_ALL; + } + return block.getNsweNearest(geoX, geoY, worldZ); + } + + /** + * Check if world coordinates has geo. + * @param worldX : World X + * @param worldY : World Y + * @return boolean : True, if given world coordinates have geodata + */ + public boolean hasGeo(int worldX, int worldY) + { + return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param loc : The location used as reference. + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(Location loc) + { + return getHeightNearest(getGeoX(loc.getX()), getGeoY(loc.getY()), loc.getZ()); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param worldX : world x + * @param worldY : world y + * @param worldZ : world z + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(int worldX, int worldY, int worldZ) + { + return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); + } + + /** + * Check line of sight from {@link WorldObject} to {@link WorldObject}.
+ * @param object : The origin object. + * @param target : The target object. + * @return True, when object can see target. + */ + public boolean canSeeTarget(WorldObject object, WorldObject target) + { + // Can always see doors. + if (target.isDoor()) + { + return true; + } + + if (object.getInstanceWorld() != target.getInstanceWorld()) + { + return false; + } + + if (DoorData.getInstance().checkIfDoorsBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld())) + { + return false; + } + + // Get object's and target's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + double theight = 0; + if (target instanceof Creature) + { + theight += (((Creature) target).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + return canSee(object.getX(), object.getY(), object.getZ(), oheight, target.getX(), target.getY(), target.getZ(), theight) && canSee(target.getX(), target.getY(), target.getZ(), theight, object.getX(), object.getY(), object.getZ(), oheight); + } + + /** + * Check line of sight from {@link WorldObject} to {@link Location}.
+ * Note: The check uses {@link Location}'s real Z coordinate (e.g. point above ground), not its geodata representation. + * @param object : The origin object. + * @param position : The target position. + * @return True, when object can see position. + */ + public boolean canSeeLocation(WorldObject object, Location position) + { + // Get object and location coordinates. + int ox = object.getX(); + int oy = object.getY(); + int oz = object.getZ(); + int tx = position.getX(); + int ty = position.getY(); + int tz = position.getZ(); + + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld())) + { + return false; + } + + // Get object's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + // Perform geodata check. + return canSee(ox, oy, oz, oheight, tx, ty, tz, 0) && canSee(tx, ty, tz, 0, ox, oy, oz, oheight); + } + + /** + * Simple check for origin to target visibility.
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param oheight : The height of origin, used as start point. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param theight : The height of target, used as end point. + * @return True, when origin can see target. + */ + public boolean canSee(int ox, int oy, int oz, double oheight, int tx, int ty, int tz, double theight) + { + // Get origin geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get target geodata coordinates. + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check being on same cell and layer (index). + // Note: Get index must use origin height increased by cell height, the method returns index to height exclusive self. + int index = block.getIndexBelow(gox, goy, oz + GeoStructure.CELL_HEIGHT); + if (index < 0) + { + return false; + } + + if ((gox == gtx) && (goy == gty)) + { + return index == block.getIndexBelow(gtx, gty, tz + GeoStructure.CELL_HEIGHT); + } + + // Get ground and nswe flag. + int groundZ = block.getHeight(index); + int nswe = block.getNswe(index); + + // Get delta coordinates, slope of line (XY, XZ) and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double dz = (tz + theight) - (oz + oheight); + final double m = (double) dy / dx; + final double mz = dz / Math.sqrt((dx * dx) + (dy * dy)); + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); + + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) + { + // Set next cell in X direction. + gridX += mdt.getStepX(); + gox += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); - if (!canSeeThrough) - { - return false; - } + // Set next cell in Y direction. + gridY += mdt.getStepY(); + goy += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevGeoZ = curGeoZ; - ++ptIndex; + // Get block of the next cell. + block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get line of sight height (including Z slope). + double losz = oz + oheight + Config.MAX_OBSTACLE_HEIGHT; + losz += mz * Math.sqrt(((checkX - ox) * (checkX - ox)) + ((checkY - oy) * (checkY - oy))); + + // Check line of sight going though wall (vertical check). + + // Get index of particular layer, based on last iterated cell conditions. + boolean canMove = (nswe & dir) != 0; + if (canMove) + { + // No wall present, get next cell below current cell. + index = block.getIndexBelow(gox, goy, groundZ + GeoStructure.CELL_IGNORE_HEIGHT); + } + else + { + // Wall present, get next cell above current cell. + index = block.getIndexAbove(gox, goy, groundZ - (2 * GeoStructure.CELL_HEIGHT)); + } + + // Next cell's does not exist (no geodata with valid condition), return fail. + if (index < 0) + { + return false; + } + + // Get next cell's layer height. + int z = block.getHeight(index); + + // Perform sine of sight check (next cell is above line of sight line), return fail. + if (z > losz) + { + return false; + } + + // Next cell is accessible, update z and NSWE. + groundZ = z; + nswe = block.getNswe(index); } + // Iteration is completed, no obstacle is found. return true; } /** - * Move check. - * @param x the x coordinate - * @param y the y coordinate - * @param zValue the z coordinate - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tzValue the target's z coordinate - * @param instance the instance - * @return the last Location (x,y,z) where player can walk - just before wall + * Check movement from coordinates to coordinates.
+ * Note: The Z coordinates are supposed to be already validated geodata coordinates. + * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return True, when target coordinates are reachable from origin coordinates. */ - public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(x); - final int geoY = getGeoY(y); - final int z = getNearestZ(geoX, geoY, zValue); - final int tGeoX = getGeoX(tx); - final int tGeoY = getGeoY(ty); - final int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return new Location(x, y, getHeight(x, y, z)); - } - if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) - { - return new Location(x, y, getHeight(x, y, z)); + return false; } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = z; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return false; + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + int goz = getHeightNearest(gox, goy, oz); + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check movement within same cell. + if ((gox == gtx) && (goy == gty)) + { + return goz == getHeight(tx, ty, tz); + } + + // Get nswe flag. + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - // Can't move, return previous location. - return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check point heading into obstacle, if so return current point. + if ((nswe & dir) == 0) + { + return false; + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return false; + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != tz)) - { - // Different floors, return start location. - return new Location(x, y, z); - } - - return new Location(tx, ty, tz); + // When origin Z is target Z, the move is successful. + return goz == getHeight(tx, ty, tz); } /** - * Checks if its possible to move from one location to another. - * @param fromX the X coordinate to start checking from - * @param fromY the Y coordinate to start checking from - * @param fromZvalue the Z coordinate to start checking from - * @param toX the X coordinate to end checking at - * @param toY the Y coordinate to end checking at - * @param toZvalue the Z coordinate to end checking at - * @param instance the instance - * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + * Check movement from origin to target coordinates. Returns last available point in the checked path.
+ * Target X and Y reachable and Z is on same floor: + *
    + *
  • Location of the target with corrected Z value from geodata.
  • + *
+ * Target X and Y reachable but Z is on another floor: + *
    + *
  • Location of the origin with corrected Z value from geodata.
  • + *
+ * Target X and Y not reachable: + *
    + *
  • Last accessible location in destination to target.
  • + *
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return The {@link Location} representing last point of movement (e.g. just before wall). */ - public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + public Location getValidLocation(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(fromX); - final int geoY = getGeoY(fromY); - final int fromZ = getNearestZ(geoX, geoY, fromZvalue); - final int tGeoX = getGeoX(toX); - final int tGeoY = getGeoY(toY); - final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); - - if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) - { - return false; + return new Location(ox, oy, oz); } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = fromZ; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return new Location(ox, oy, oz); + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + final int gtz = getHeightNearest(gtx, gty, tz); + int goz = getHeightNearest(gox, goy, oz); + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - return false; - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check target cell is out of geodata grid (world coordinates). + if ((nx < 0) || (nx >= GeoStructure.GEO_CELLS_X) || (ny < 0) || (ny >= GeoStructure.GEO_CELLS_Y)) + { + return new Location(checkX, checkY, goz); + } + + // Check point heading into obstacle, if so return current (border) point. + if ((nswe & dir) == 0) + { + return new Location(checkX, checkY, goz); + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current (border) point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return new Location(checkX, checkY, goz); + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) - { - // Different floors. - return false; - } - - return true; + // Compare Z coordinates: + // If same, path is okay, return target point and fix its Z geodata coordinate. + // If not same, path is does not exist, return origin point. + return goz == gtz ? new Location(tx, ty, gtz) : new Location(ox, oy, oz); } + /** + * Returns the list of location objects as a result of complete path calculation. + * @param ox : origin x + * @param oy : origin y + * @param oz : origin z + * @param tx : target x + * @param ty : target y + * @param tz : target z + * @param instance + * @return {@code List} : complete path from nodes + */ + public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + { + // Get origin and check existing geo coords. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + if (!hasGeoPos(gox, goy)) + { + return Collections.emptyList(); + } + + int goz = getHeightNearest(gox, goy, oz); + + // Get target and check existing geo coords. + int gtx = getGeoX(tx); + int gty = getGeoY(ty); + if (!hasGeoPos(gtx, gty)) + { + return Collections.emptyList(); + } + + int gtz = getHeightNearest(gtx, gty, tz); + + // Prepare buffer for pathfinding calculations. + NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + if (buffer == null) + { + return Collections.emptyList(); + } + + // Find path. + List path = null; + try + { + path = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + if (path.isEmpty()) + { + return Collections.emptyList(); + } + } + catch (Exception e) + { + return Collections.emptyList(); + } + finally + { + buffer.free(); + } + + // Check path. + if (path.size() < 3) + { + return path; + } + + // Get path list iterator. + ListIterator point = path.listIterator(); + + // Get node A (origin). + int nodeAx = ox; + int nodeAy = oy; + int nodeAz = goz; + + // Get node B. + Location nodeB = point.next(); + + // Iterate thought the path to optimize it. + while (point.hasNext()) + { + // Get node C. + Location nodeC = path.get(point.nextIndex()); + + // Check movement from node A to node C. + if (canMoveToTarget(nodeAx, nodeAy, nodeAz, nodeC.getX(), nodeC.getY(), nodeC.getZ(), instance)) + { + // Can move from node A to node C. + // Remove node B. + point.remove(); + } + else + { + // Can not move from node A to node C. + // Set node A (node B is part of path, update A coordinates). + nodeAx = nodeB.getX(); + nodeAy = nodeB.getY(); + nodeAz = nodeB.getZ(); + } + + // Set node B. + nodeB = point.next(); + } + + return path; + } + + /** + * NodeBuffer container with specified size and count of separate buffers. + */ + private static class BufferHolder + { + final int _size; + final List _buffer; + + public BufferHolder(int size, int count) + { + _size = size; + _buffer = new ArrayList<>(count); + + for (int i = 0; i < count; i++) + { + _buffer.add(new NodeBuffer(size)); + } + } + } + + /** + * Returns the instance of the {@link GeoEngine}. + * @return {@link GeoEngine} : The instance. + */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -737,4 +1054,4 @@ public class GeoEngine { protected static final GeoEngine INSTANCE = new GeoEngine(); } -} +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java deleted file mode 100644 index cc76b7523a..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ /dev/null @@ -1,301 +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 . - */ -package org.l2jmobius.gameserver.geoengine; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; -import org.l2jmobius.gameserver.model.World; -import org.l2jmobius.gameserver.model.instancezone.Instance; - -/** - * @author -Nemesiss- - */ -public class GeoEnginePathfinding -{ - private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); - - private BufferInfo[] _buffers; - - protected GeoEnginePathfinding() - { - try - { - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - - _buffers = 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); - } - - _buffers[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"); - } - } - - public boolean pathNodesExist(short regionoffset) - { - return false; - } - - public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) - { - 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)))); - if (buffer == null) - { - return null; - } - - List path = null; - try - { - final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); - - if (result == null) - { - return null; - } - - path = constructPath(result); - } - catch (Exception e) - { - LOGGER.warning(e.getMessage()); - return null; - } - finally - { - buffer.free(); - } - - // check path - if (path.size() < 3) - { - return path; - } - - int currentX, currentY, currentZ; - ListIterator middlePoint; - - middlePoint = path.listIterator(); - currentX = x; - currentY = y; - currentZ = z; - - while (middlePoint.hasNext()) - { - final AbstractNodeLoc locMiddle = middlePoint.next(); - if (!middlePoint.hasNext()) - { - break; - } - - final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); - if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) - { - middlePoint.remove(); - } - else - { - currentX = locMiddle.getX(); - currentY = locMiddle.getY(); - currentZ = locMiddle.getZ(); - } - } - - return path; - } - - /** - * Convert geodata position to pathnode position - * @param geo_pos - * @return pathnode position - */ - public short getNodePos(int geo_pos) - { - return (short) (geo_pos >> 3); // OK? - } - - /** - * Convert node position to pathnode block position - * @param node_pos - * @return pathnode block position (0...255) - */ - public short getNodeBlock(int node_pos) - { - return (short) (node_pos % 256); - } - - public byte getRegionX(int node_pos) - { - return (byte) ((node_pos >> 8) + World.TILE_X_MIN); - } - - public byte getRegionY(int node_pos) - { - return (byte) ((node_pos >> 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 node_x rx - * @return - */ - public int calculateWorldX(short node_x) - { - return World.MAP_MIN_X + (node_x * 128) + 48; - } - - /** - * Convert pathnode y to World y position - * @param node_y - * @return - */ - public int calculateWorldY(short node_y) - { - return World.MAP_MIN_Y + (node_y * 128) + 48; - } - - private List constructPath(AbstractNode nodeValue) - { - final LinkedList path = new LinkedList<>(); - int previousDirectionX = Integer.MIN_VALUE; - int previousDirectionY = Integer.MIN_VALUE; - int directionX, directionY; - - AbstractNode node = nodeValue; - while (node.getParent() != null) - { - directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); - directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); - - // only add a new route point if moving direction changes - if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) - { - previousDirectionX = directionX; - previousDirectionY = directionY; - - path.addFirst(node.getLoc()); - node.setLoc(null); - } - - node = node.getParent(); - } - - return path; - } - - private CellNodeBuffer alloc(int size) - { - CellNodeBuffer current = null; - for (BufferInfo i : _buffers) - { - if (i.mapSize >= size) - { - for (CellNodeBuffer buf : i.buffer) - { - if (buf.lock()) - { - current = buf; - break; - } - } - if (current != null) - { - break; - } - - // not found, allocate temporary buffer - current = new CellNodeBuffer(i.mapSize); - current.lock(); - if (i.buffer.size() < i.count) - { - i.buffer.add(current); - break; - } - } - } - - return current; - } - - private static final class BufferInfo - { - final int mapSize; - final int count; - ArrayList buffer; - - public BufferInfo(int size, int cnt) - { - mapSize = size; - count = cnt; - buffer = new ArrayList<>(count); - } - } - - public static GeoEnginePathfinding getInstance() - { - return SingletonHolder.INSTANCE; - } - - private static class SingletonHolder - { - protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); - } -} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java new file mode 100644 index 0000000000..49ec35aa1c --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java @@ -0,0 +1,85 @@ +/* + * 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 . + */ +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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @param index : Index of the cell. + * @return short : Cell geodata Z coordinate, below given coordinates. + */ + public abstract byte getNswe(int index); +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java new file mode 100644 index 0000000000..f5997bf287 --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java @@ -0,0 +1,130 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java new file mode 100644 index 0000000000..9af9d2a28a --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java @@ -0,0 +1,95 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java new file mode 100644 index 0000000000..31fbf5d477 --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java new file mode 100644 index 0000000000..f77d21d84b --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java @@ -0,0 +1,68 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +public class BlockNull extends ABlock +{ + @Override + public boolean hasGeoPos() + { + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java deleted file mode 100644 index 61ea4fcf82..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java +++ /dev/null @@ -1,48 +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 . - */ -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() - { - } -} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java deleted file mode 100644 index 3c8de1a7f7..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java +++ /dev/null @@ -1,77 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java deleted file mode 100644 index 1ccdefe3da..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java +++ /dev/null @@ -1,56 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java new file mode 100644 index 0000000000..691b164e0c --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ +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_ALL = 0x0F; + + // 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; +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java deleted file mode 100644 index 25eafc5a04..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java +++ /dev/null @@ -1,42 +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 . - */ -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); -} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java deleted file mode 100644 index 0d2c765002..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java +++ /dev/null @@ -1,47 +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 . - */ -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(); -} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java deleted file mode 100644 index 52df457360..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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); - - // 1 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; - } -} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java deleted file mode 100644 index 72a5a635c7..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java +++ /dev/null @@ -1,55 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author HorridoJoho - */ -public final class NullRegion implements IRegion -{ - public static final NullRegion INSTANCE = new NullRegion(); - - @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 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; - } -} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java deleted file mode 100644 index fc924cb875..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java +++ /dev/null @@ -1,92 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java deleted file mode 100644 index 5087513ed9..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java +++ /dev/null @@ -1,87 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public abstract class AbstractNode -{ - private T _loc; - private AbstractNode _parent; - - public AbstractNode(T loc) - { - _loc = loc; - } - - public void setParent(AbstractNode p) - { - _parent = p; - } - - public AbstractNode getParent() - { - return _parent; - } - - public T getLoc() - { - return _loc; - } - - public void setLoc(T l) - { - _loc = l; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); - return result; - } - - @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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java deleted file mode 100644 index 7223b5b2be..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java +++ /dev/null @@ -1,33 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -/** - * @author -Nemesiss- - */ -public abstract class AbstractNodeLoc -{ - public abstract int getX(); - - public abstract int getY(); - - public abstract int getZ(); - - public abstract int getNodeX(); - - public abstract int getNodeY(); -} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java deleted file mode 100644 index 3425234e0e..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public class CellNode extends AbstractNode -{ - 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java deleted file mode 100644 index 522610bb7c..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java +++ /dev/null @@ -1,348 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -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 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) - { - _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(); - } - - public final List debugPath() - { - final List 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 void getNeighbors() - { - if (!_current.getLoc().canGoAll()) - { - 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); - } - - // SouthEast - if ((nodeE != null) && (nodeS != null)) - { - if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) - { - addNode(x + 1, y + 1, z, true); - } - } - - // SouthWest - if ((nodeS != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) - { - addNode(x - 1, y + 1, z, true); - } - } - - // NorthEast - if ((nodeN != null) && (nodeE != null)) - { - if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) - { - addNode(x + 1, y - 1, z, true); - } - } - - // NorthWest - if ((nodeN != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) - { - addNode(x - 1, y - 1, z, true); - } - } - } - - private 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(); - // reinit node if needed - if (result.getLoc() != null) - { - result.getLoc().set(x, y, z); - } - else - { - result.setLoc(new NodeLoc(x, y, z)); - } - } - - return result; - } - - private 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 boolean isHighWeight(int x, int y, int z) - { - final CellNode result = getNode(x, y, z); - if (result == null) - { - return true; - } - - if (!result.getLoc().canGoAll()) - { - return true; - } - if (Math.abs(result.getLoc().getZ() - z) > 16) - { - return true; - } - - return false; - } - - private 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java new file mode 100644 index 0000000000..fa5ca43c2f --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -0,0 +1,111 @@ +/* + * 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 . + */ +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 geodata values. + private int _geoX; + private int _geoY; + private byte _nswe; + + // 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; + + _costG = 0; + _costH = 0; + _costF = 0; + + _parent = null; + } + + public void setGeo(int gx, int gy, int gz, byte nswe) + { + super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); + + _geoX = gx; + _geoY = gy; + _nswe = nswe; + } + + 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 int getCostF() + { + return _costF; + } + + public Node getParent() + { + return _parent; + } + + @Override + public int compareTo(Node o) + { + return _costF - o._costF; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java new file mode 100644 index 0000000000..b464212c96 --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -0,0 +1,368 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.awt.Color; +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; +import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; + +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 _opened; + // Container holding Nodes already explored. + private final List _closed; + + // Target coordinates. + private int _gtx; + private int _gty; + private int _gtz; + + // Pathfinding statistics. + private long _timeStamp; + private long _lastElapsedTime; + + 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 findPath(int gox, int goy, int goz, int gtx, int gty, int gtz) + { + // Set start timestamp. + _timeStamp = System.currentTimeMillis(); + + // 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)); + _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 constructPath() + { + // Create result. + final LinkedList 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; + } + + /** + * Creates list of Nodes to show debug path. + * @param debug : The debug packet to add debug informations in. + */ + public void debugPath(ExServerPrimitive debug) + { + // Add all opened node as yellow points. + for (Node n : _opened) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); + } + + // Add all opened node as blue points. + for (Node n : _closed) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); + } + } + + 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; + + _lastElapsedTime = System.currentTimeMillis() - _timeStamp; + _lock.unlock(); + } + + public long getElapsedTime() + { + return _lastElapsedTime; + } + + /** + * Expand the current {@link Node} by exploring its neighbors (axially and diagonally). + */ + private void expand() + { + // Movement is blocked, skip. + final byte nswe = _current.getNSWE(); + if (nswe == 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; + + // Can move north, expand. + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + } + + // Can move south, expand. + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + } + + // Can move west, expand. + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move east, expand. + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move north-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move north-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + } + + /** + * 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 weight : The weight of movement to the new node. + * @return The nswe of the added node. Blank, if not added. + */ + private byte addNode(int gx, int gy, int gzValue, int weight) + { + // 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]; + + // Set node geodata coordinates. + node.setGeo(gx, gy, gz, nswe); + + // 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, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + _opened.add(node); + _bufferIndex++; + return nswe; + } + + /** + * Calculate cost H value, calculated using diagonal distance method.
+ * 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. + final int dx = Math.abs(gx - _gtx); + final int dy = Math.abs(gy - _gty); + final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + + // Get diagonal and axial differences to the target. + final int dd = Math.min(dx, dy); + final int da = Math.max(dx, dy) - dd; + + // Calculate the diagonal distance of the node to the target. + return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java deleted file mode 100644 index e3bad04b25..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; - -/** - * @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 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 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; - // return super.hashCode(); - } - - @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; - if (_x != other._x) - { - return false; - } - if (_y != other._y) - { - return false; - } - if (_goNorth != other._goNorth) - { - return false; - } - if (_goEast != other._goEast) - { - return false; - } - if (_goSouth != other._goSouth) - { - return false; - } - if (_goWest != other._goWest) - { - return false; - } - if (_geoHeight != other._geoHeight) - { - return false; - } - return true; - } -} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java index 1faf1ae423..ed261765e1 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java @@ -93,15 +93,15 @@ public class ZoneManager implements IXmlReader private static final Map SETTINGS = new HashMap<>(); private static final int SHIFT_BY = 15; - private static final int OFFSET_X = Math.abs(World.MAP_MIN_X >> SHIFT_BY); - private static final int OFFSET_Y = Math.abs(World.MAP_MIN_Y >> SHIFT_BY); + private static final int OFFSET_X = Math.abs(World.WORLD_X_MIN >> SHIFT_BY); + private static final int OFFSET_Y = Math.abs(World.WORLD_Y_MIN >> SHIFT_BY); private final Map, ConcurrentHashMap> _classZones = new ConcurrentHashMap<>(); private final Map _spawnTerritories = new ConcurrentHashMap<>(); private final AtomicInteger _lastDynamicId = new AtomicInteger(300000); private List _debugItems; - private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.MAP_MAX_X >> SHIFT_BY) + OFFSET_X + 1][(World.MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y + 1]; + private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.WORLD_X_MAX >> SHIFT_BY) + OFFSET_X + 1][(World.WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y + 1]; /** * Instantiates a new zone manager. diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/Location.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/Location.java index 73689ab1a6..ca3b22a3f0 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/Location.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/Location.java @@ -16,30 +16,30 @@ */ package org.l2jmobius.gameserver.model; +import java.util.Objects; + +import org.l2jmobius.commons.util.Point2D; import org.l2jmobius.gameserver.model.interfaces.ILocational; import org.l2jmobius.gameserver.model.interfaces.IPositionable; /** - * Location data transfer object.
- * Contains coordinates data, heading and instance Id. - * @author Zoey76 + * A datatype used to retain a 3D (x/y/z/heading) point. It got the capability to be set and cleaned. */ -public class Location implements IPositionable +public class Location extends Point2D implements IPositionable { - protected int _x; - protected int _y; - protected int _z; - private int _heading; + protected volatile int _z; + protected volatile int _heading; public Location(int x, int y, int z) { - this(x, y, z, 0); + super(x, y); + _z = z; + _heading = 0; } public Location(int x, int y, int z, int heading) { - _x = x; - _y = y; + super(x, y); _z = z; _heading = heading; } @@ -51,9 +51,8 @@ public class Location implements IPositionable public Location(StatSet set) { - _x = set.getInt("x"); - _y = set.getInt("y"); - _z = set.getInt("z"); + super(set.getInt("x", 0), set.getInt("y", 0)); + _z = set.getInt("z", 0); _heading = set.getInt("heading", 0); } @@ -146,6 +145,25 @@ public class Location implements IPositionable _heading = loc.getHeading(); } + @Override + public void clean() + { + super.clean(); + _z = 0; + } + + @Override + public Location clone() + { + return new Location(_x, _y, _z); + } + + @Override + public int hashCode() + { + return (31 * super.hashCode()) + Objects.hash(_z); + } + @Override public boolean equals(Object obj) { diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/World.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/World.java index c1510e8c0e..0d796dd834 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/World.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/World.java @@ -66,19 +66,19 @@ public class World public static final int TILE_Y_MAX = 26; public static final int TILE_ZERO_COORD_X = 20; public static final int TILE_ZERO_COORD_Y = 18; - public static final int MAP_MIN_X = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; - public static final int MAP_MIN_Y = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; + public static final int WORLD_X_MIN = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; + public static final int WORLD_Y_MIN = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; - public static final int MAP_MAX_X = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; - public static final int MAP_MAX_Y = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; + public static final int WORLD_X_MAX = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; + public static final int WORLD_Y_MAX = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; /** Calculated offset used so top left region is 0,0 */ - public static final int OFFSET_X = Math.abs(MAP_MIN_X >> SHIFT_BY); - public static final int OFFSET_Y = Math.abs(MAP_MIN_Y >> SHIFT_BY); + public static final int OFFSET_X = Math.abs(WORLD_X_MIN >> SHIFT_BY); + public static final int OFFSET_Y = Math.abs(WORLD_Y_MIN >> SHIFT_BY); /** Number of regions. */ - private static final int REGIONS_X = (MAP_MAX_X >> SHIFT_BY) + OFFSET_X; - private static final int REGIONS_Y = (MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y; + private static final int REGIONS_X = (WORLD_X_MAX >> SHIFT_BY) + OFFSET_X; + private static final int REGIONS_Y = (WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y; /** Map containing all the players in game. */ private static final Map _allPlayers = new ConcurrentHashMap<>(); @@ -817,9 +817,6 @@ public class World return _memberInPartyNumber.get(); } - /** - * @return the current instance of World - */ public static World getInstance() { return SingletonHolder.INSTANCE; diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/WorldObject.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/WorldObject.java index 0e5536bbc0..62313ffea1 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/WorldObject.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/WorldObject.java @@ -188,23 +188,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif synchronized (this) { int spawnX = x; - if (spawnX > World.MAP_MAX_X) + if (spawnX > World.WORLD_X_MAX) { - spawnX = World.MAP_MAX_X - 5000; + spawnX = World.WORLD_X_MAX - 5000; } - if (spawnX < World.MAP_MIN_X) + if (spawnX < World.WORLD_X_MIN) { - spawnX = World.MAP_MIN_X + 5000; + spawnX = World.WORLD_X_MIN + 5000; } int spawnY = y; - if (spawnY > World.MAP_MAX_Y) + if (spawnY > World.WORLD_Y_MAX) { - spawnY = World.MAP_MAX_Y - 5000; + spawnY = World.WORLD_Y_MAX - 5000; } - if (spawnY < World.MAP_MIN_Y) + if (spawnY < World.WORLD_Y_MIN) { - spawnY = World.MAP_MIN_Y + 5000; + spawnY = World.WORLD_Y_MIN + 5000; } // Set the x,y,z position of the WorldObject. If flagged with _isSpawned, setXYZ will automatically update world region, so avoid that. @@ -518,23 +518,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif public void setXYZInvisible(int x, int y, int z) { int correctX = x; - if (correctX > World.MAP_MAX_X) + if (correctX > World.WORLD_X_MAX) { - correctX = World.MAP_MAX_X - 5000; + correctX = World.WORLD_X_MAX - 5000; } - if (correctX < World.MAP_MIN_X) + if (correctX < World.WORLD_X_MIN) { - correctX = World.MAP_MIN_X + 5000; + correctX = World.WORLD_X_MIN + 5000; } int correctY = y; - if (correctY > World.MAP_MAX_Y) + if (correctY > World.WORLD_Y_MAX) { - correctY = World.MAP_MAX_Y - 5000; + correctY = World.WORLD_Y_MAX - 5000; } - if (correctY < World.MAP_MIN_Y) + if (correctY < World.WORLD_Y_MIN) { - correctY = World.MAP_MIN_Y + 5000; + correctY = World.WORLD_Y_MIN + 5000; } setXYZ(correctX, correctY, z); diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/Creature.java index 32f014e233..acf6ab07ff 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -66,8 +66,6 @@ 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.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -2586,7 +2584,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3419,8 +3417,8 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe final int originalX = x; final int originalY = y; final int originalZ = z; - final int gtx = (originalX - World.MAP_MIN_X) >> 4; - final int gty = (originalY - World.MAP_MIN_Y) >> 4; + final int gtx = (originalX - World.WORLD_X_MIN) >> 4; + final int gty = (originalY - World.WORLD_Y_MIN) >> 4; if (isOnGeodataPath()) { try @@ -3443,7 +3441,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe && !(((curZ - z) > 300) && (distance < 300))) // Prohibit correcting destination if character wants to fall. { // location different if destination wasn't reached (or just z coord is different) - final Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(curX, curY, curZ, x, y, z, getInstanceWorld()); + final Location destiny = GeoEngine.getInstance().getValidLocation(curX, curY, curZ, x, y, z, getInstanceWorld()); x = destiny.getX(); y = destiny.getY(); z = destiny.getZ(); @@ -3457,7 +3455,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { if (isPlayer() && !_isFlying && !isInWater) diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/Summon.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/Summon.java index 03d1af23ea..be89eb6407 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/Summon.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/actor/Summon.java @@ -106,7 +106,7 @@ public abstract class Summon extends Playable final int x = owner.getX(); final int y = owner.getY(); final int z = owner.getZ(); - final Location location = GeoEngine.getInstance().canMoveToTargetLoc(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, owner.getInstanceWorld()); + final Location location = GeoEngine.getInstance().getValidLocation(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, getInstanceWorld()); setXYZInvisible(location.getX(), location.getY(), location.getZ()); } diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java index abaec9036f..20cd1966d5 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java @@ -1560,7 +1560,7 @@ public class ItemInstance extends WorldObject if (dropper != null) { final Instance instance = dropper.getInstanceWorld(); - final Location dropDest = GeoEngine.getInstance().canMoveToTargetLoc(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); + final Location dropDest = GeoEngine.getInstance().getValidLocation(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); x = dropDest.getX(); y = dropDest.getY(); z = dropDest.getZ(); diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java index 5d294fe47c..558bf92edf 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java @@ -1179,7 +1179,7 @@ public class SkillCaster implements Runnable } } - final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().canMoveToTargetLoc(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); + final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().getValidLocation(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); creature.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); creature.broadcastPacket(new FlyToLocation(creature, destination, flyType, 0, 0, 333)); diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java index c62c821f44..30068134e3 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java @@ -114,7 +114,7 @@ public class ValidatePosition implements IClientIncomingPacket { if (player.isFalling(_z)) { - final int nearestZ = GeoEngine.getInstance().getHigherHeight(_x, _y, _z); + final int nearestZ = GeoEngine.getInstance().getHeight(_x, _y, _z); if (player.getZ() < nearestZ) { player.setXYZ(_x, _y, nearestZ); diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/util/GeoUtils.java index 4600a0fc53..19a979819e 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -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.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; @@ -30,21 +30,21 @@ public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getInstance().getWorldX(iter.x()); - final int wy = GeoEngine.getInstance().getWorldY(iter.y()); + final int wx = GeoEngine.getWorldX(iter.x()); + final int wy = GeoEngine.getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public final class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); - int wy = GeoEngine.getInstance().getWorldY(prevY); + int wx = GeoEngine.getWorldX(prevX); + int wy = GeoEngine.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.getInstance().getWorldX(curX); - wy = GeoEngine.getInstance().getWorldY(curY); + wx = GeoEngine.getWorldX(curX); + wy = GeoEngine.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().checkNearestNswe(x, y, z, nswe)) + if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0) { return Color.GREEN; } @@ -109,9 +109,8 @@ public final class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final GeoEngine ge = GeoEngine.getInstance(); - final int playerGx = ge.getGeoX(player.getX()); - final int playerGy = ge.getGeoY(player.getY()); + final int playerGx = GeoEngine.getGeoX(player.getX()); + final int playerGy = GeoEngine.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -135,32 +134,32 @@ public final class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = ge.getWorldX(gx); - final int y = ge.getWorldY(gy); - final int z = ge.getNearestZ(gx, gy, player.getZ()); + final int x = GeoEngine.getWorldX(gx); + final int y = GeoEngine.getWorldY(gy); + final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); + Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); 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, Cell.NSWE_EAST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); 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, Cell.NSWE_SOUTH); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); 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, Cell.NSWE_WEST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); 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); @@ -188,41 +187,41 @@ public final class GeoUtils { if (y > lastY) { - return Cell.NSWE_SOUTH_EAST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E; } else if (y < lastY) { - return Cell.NSWE_NORTH_EAST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E; } else { - return Cell.NSWE_EAST; + return GeoStructure.CELL_FLAG_E; } } else if (x < lastX) // west { if (y > lastY) { - return Cell.NSWE_SOUTH_WEST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W; } else if (y < lastY) { - return Cell.NSWE_NORTH_WEST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W; } else { - return Cell.NSWE_WEST; + return GeoStructure.CELL_FLAG_W; } } else // unchanged x { if (y > lastY) { - return Cell.NSWE_SOUTH; + return GeoStructure.CELL_FLAG_S; } else if (y < lastY) { - return Cell.NSWE_NORTH; + return GeoStructure.CELL_FLAG_N; } else { diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/config/GeoEngine.ini b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/config/GeoEngine.ini index 609e8a89a7..e740c678dd 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/config/GeoEngine.ini @@ -6,33 +6,44 @@ # at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ 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 + # ================================================================= -# Path finding +# Pathfinding # ================================================================= # 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 +# an alternative path (e.g. walk around obstacle), default: True +PathFinding = True -# Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 -PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 +# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 -# Weight for nodes without obstacles far from walls -LowWeight = 0.5 +# Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 +MoveWeight = 10 +MoveWeightDiag = 14 -# Weight for nodes near walls -MediumWeight = 2 +# 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 -# Weight for nodes with obstacles -HighWeight = 3 +# 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. -# Default: LowWeight * sqrt(2) -DiagonalWeight = 0.707 +# Maximum number of generated nodes per one path-finding process, default 3500 +MaxIterations = 3500 # ================================================================= -# Other +# Line of Sight # ================================================================= -# Correct player Z after falling through the ground. -CorrectPlayerZ = False +# 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 diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/geodata/L2D_GeoEngine.patch b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/geodata/L2D_GeoEngine.patch deleted file mode 100644 index f8577e3a0e..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/geodata/L2D_GeoEngine.patch +++ /dev/null @@ -1,6035 +0,0 @@ -Index: dist/game/GeoDataConverter.bat -=================================================================== ---- dist/game/GeoDataConverter.bat (nonexistent) -+++ dist/game/GeoDataConverter.bat (working copy) -@@ -0,0 +1,6 @@ -+@echo off -+title L2D geodata converter -+ -+java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter -+ -+pause -Index: dist/game/GeoDataConverter.sh -=================================================================== ---- dist/game/GeoDataConverter.sh (nonexistent) -+++ dist/game/GeoDataConverter.sh (working copy) -@@ -0,0 +1,4 @@ -+#! /bin/sh -+ -+java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 -+ -Index: dist/game/config/GeoEngine.ini -=================================================================== ---- dist/game/config/GeoEngine.ini (revision 8409) -+++ dist/game/config/GeoEngine.ini (working copy) -@@ -1,6 +1,10 @@ - # ================================================================= - # Geodata - # ================================================================= -+# Because of real-time performance we are using geodata files only in -+# diagonal L2D format now (using filename e.g. 22_16.l2d). -+# L2D geodata can be obtained by conversion of existing L2J or L2OFF geodata. -+# Launch "GeoDataConverter.bat/sh" and follow instructions to start the conversion. - - # 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/ -@@ -13,6 +17,16 @@ - CoordSynchronize = 2 - - # ================================================================= -+# Path checking -+# ================================================================= -+ -+# 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 finding - # ================================================================= - -@@ -23,19 +37,23 @@ - # Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - --# Weight for nodes without obstacles far from walls --LowWeight = 0.5 -+# Base path weight, when moving from one node to another on axis direction, default: 10 -+BaseWeight = 10 - --# Weight for nodes near walls --MediumWeight = 2 -+# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -+DiagonalWeight = 14 - --# Weight for nodes with obstacles --HighWeight = 3 -+# When movement flags of target node is blocked to any direction, multiply movement weight by this multiplier. -+# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 10 -+ObstacleMultiplier = 10 - --# Weight for diagonal movement. --# Default: LowWeight * sqrt(2) --DiagonalWeight = 0.707 -+# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 20 -+# For proper function must be higher than BaseWeight and/or DiagonalWeight. -+HeuristicWeight = 20 - -+# Maximum number of generated nodes per one path-finding process, default 3500 -+MaxIterations = 3500 -+ - # ================================================================= - # Other - # ================================================================= -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (working copy) -@@ -55,9 +55,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -@@ -73,9 +72,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (working copy) -@@ -18,11 +18,11 @@ - - import java.util.List; - --import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -+import org.l2jmobius.gameserver.geoengine.GeoEngine; - import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -+import org.l2jmobius.gameserver.network.SystemMessageId; - import org.l2jmobius.gameserver.util.BuilderUtil; - - public class AdminPathNode implements IAdminCommandHandler -@@ -37,29 +37,30 @@ - { - if (command.equals("admin_path_find")) - { -- if (!Config.PATHFINDING) -- { -- BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); -- return true; -- } - if (activeChar.getTarget() != null) - { -- final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); -+ final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); - if (path == null) - { -- BuilderUtil.sendSysMessage(activeChar, "No Route!"); -- return true; -+ BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); - } -- for (AbstractNodeLoc a : path) -+ else - { -- BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); -+ for (Location point : path) -+ { -+ BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); -+ } - } - } - else - { -- BuilderUtil.sendSysMessage(activeChar, "No Target!"); -+ activeChar.sendPacket(SystemMessageId.INVALID_TARGET); - } - } -+ else -+ { -+ return false; -+ } - return true; - } - -Index: java/org/l2jmobius/Config.java -=================================================================== ---- java/org/l2jmobius/Config.java (revision 8409) -+++ java/org/l2jmobius/Config.java (working copy) -@@ -32,7 +32,6 @@ - import java.net.UnknownHostException; - import java.nio.charset.StandardCharsets; - import java.nio.file.Files; --import java.nio.file.Path; - import java.nio.file.Paths; - import java.time.Duration; - import java.util.ArrayList; -@@ -966,14 +965,17 @@ - // -------------------------------------------------- - // GeoEngine - // -------------------------------------------------- -- public static Path GEODATA_PATH; -+ public static String GEODATA_PATH; -+ public static int COORD_SYNCHRONIZE; -+ public static int PART_OF_CHARACTER_HEIGHT; -+ public static int MAX_OBSTACLE_HEIGHT; - public static boolean PATHFINDING; - public static String PATHFIND_BUFFERS; -- public static float LOW_WEIGHT; -- public static float MEDIUM_WEIGHT; -- public static float HIGH_WEIGHT; -- public static float DIAGONAL_WEIGHT; -- public static int COORD_SYNCHRONIZE; -+ public static int BASE_WEIGHT; -+ public static int DIAGONAL_WEIGHT; -+ public static int HEURISTIC_WEIGHT; -+ public static int OBSTACLE_MULTIPLIER; -+ public static int MAX_ITERATIONS; - public static boolean CORRECT_PLAYER_Z; - - /** Attribute System */ -@@ -2568,14 +2570,17 @@ - - // Load GeoEngine config file (if exists) - final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); -- GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); -+ GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); -+ COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); -+ MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); - PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -- LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); -- MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); -- HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); -- DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); -- COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); -+ DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); -+ OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); -+ HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); -+ MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); - CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); - - // Load AllowedPlayerRaces config file (if exists) -Index: java/org/l2jmobius/gameserver/geoengine/GeoEngine.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (revision 8435) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (working copy) -@@ -16,100 +16,90 @@ - */ - package org.l2jmobius.gameserver.geoengine; - --import java.io.IOException; -+import java.io.File; - import java.io.RandomAccessFile; - import java.nio.ByteOrder; --import java.nio.channels.FileChannel.MapMode; --import java.nio.file.Files; --import java.nio.file.Path; --import java.util.concurrent.atomic.AtomicReferenceArray; --import java.util.logging.Level; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.List; - import java.util.logging.Logger; - - import org.l2jmobius.Config; - import org.l2jmobius.gameserver.data.xml.DoorData; - import org.l2jmobius.gameserver.data.xml.FenceData; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; --import org.l2jmobius.gameserver.geoengine.geodata.IRegion; --import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; --import org.l2jmobius.gameserver.geoengine.geodata.Region; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.World; - import org.l2jmobius.gameserver.model.WorldObject; -+import org.l2jmobius.gameserver.model.actor.Creature; - import org.l2jmobius.gameserver.model.instancezone.Instance; --import org.l2jmobius.gameserver.model.interfaces.ILocational; --import org.l2jmobius.gameserver.util.GeoUtils; --import org.l2jmobius.gameserver.util.LinePointIterator; --import org.l2jmobius.gameserver.util.LinePointIterator3D; -+import org.l2jmobius.gameserver.util.MathUtil; - - /** -- * @author -Nemesiss-, HorridoJoho -+ * @author Hasha - */ - public class GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); -+ protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - -- private static final int WORLD_MIN_X = -655360; -- private static final int WORLD_MIN_Y = -589824; -- private static final int WORLD_MIN_Z = -16384; -+ private final ABlock[][] _blocks; -+ private final BlockNull _nullBlock; - -- /** 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; -- -- /** The regions array */ -- private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); -- -- private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; -- private static final int ELEVATED_SEE_OVER_DISTANCE = 2; -- private static final int MAX_SEE_OVER_HEIGHT = 48; -- -- protected GeoEngine() -+ /** -+ * GeoEngine constructor. Loads all geodata files of chosen geodata format. -+ */ -+ public GeoEngine() - { - LOGGER.info("GeoEngine: Initializing..."); -- for (int i = 0; i < _regions.length(); i++) -- { -- _regions.set(i, NullRegion.INSTANCE); -- } - -+ // initialize block container -+ _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; -+ -+ // load null block -+ _nullBlock = new BlockNull(); -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files according to geoengine config setup - int loaded = 0; -- try -+ long fileSize = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { -- for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { -- for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) -+ final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); -+ if (f.exists() && !f.isDirectory()) - { -- final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); -- if (Files.exists(geoFilePath)) -+ // region file is load-able, try to load it -+ if (loadGeoBlocks(rx, ry)) - { -- try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) -- { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -- loaded++; -- } -+ loaded++; -+ fileSize += f.length(); - } - } -+ else -+ { -+ // region file is not load-able, load null blocks -+ loadNullBlocks(rx, ry); -+ } - } - } -- catch (Exception e) -+ -+ LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -+ if (loaded > 0) - { -- LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); -- System.exit(1); -+ LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); - } - -- LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -- -- // Avoid wrong configs when no files are loaded. -+ // avoid wrong configs when no files are loaded - if (loaded == 0) - { - if (Config.PATHFINDING) -@@ -123,619 +113,820 @@ - LOGGER.info("GeoEngine: Forcing CoordSynchronize setting to -1."); - } - } -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); - } - - /** -- * @param geoX -- * @param geoY -- * @return the region -+ * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. -+ * @return boolean : True, when geodata file was loaded without problem. - */ -- private IRegion getRegion(int geoX, int geoY) -+ private final boolean loadGeoBlocks(int regionX, int regionY) - { -- final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); -- if ((region < 0) || (region >= _regions.length())) -+ final String filename = String.format(GeoFormat.L2D.getFilename(), regionX, regionY); -+ -+ // standard load -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) - { -- return null; -+ // initialize file buffer -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, GeoFormat.L2D); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ } -+ -+ // check data consistency -+ if (buffer.remaining() > 0) -+ { -+ LOGGER.warning("GeoEngine: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ } -+ -+ // loading was successful -+ return true; - } -- return _regions.get(region); -- } -- -- /** -- * @param filePath -- * @param regionX -- * @param regionY -- * @throws IOException -- */ -- 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")) -+ catch (Exception e) - { -- _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -+ // an error occured while loading, load null blocks -+ LOGGER.warning("GeoEngine: Error while loading " + filename + " region file."); -+ LOGGER.warning(e.getMessage()); -+ -+ // replace whole region file with null blocks -+ loadNullBlocks(regionX, regionY); -+ -+ // loading was not successful -+ return false; - } - } - - /** -- * @param regionX -- * @param regionY -+ * Loads null blocks. Used when no region file is detected or an error occurs during loading. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. - */ -- public void unloadRegion(int regionX, int regionY) -+ private final void loadNullBlocks(int regionX, int regionY) - { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); -- } -- -- /** -- * @param geoX -- * @param geoY -- * @return if geodata exist -- */ -- public boolean hasGeoPos(int geoX, int geoY) -- { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // load all null blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { -- return false; -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[blockX + ix][blockY + iy] = _nullBlock; -+ } - } -- return region.hasGeo(); - } - -+ // GEODATA - GENERAL -+ - /** -- * Checks the specified position for available geodata. -- * @param x the world x -- * @param y the world y -- * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise -+ * Converts world X to geodata X. -+ * @param worldX -+ * @return int : Geo X - */ -- public boolean hasGeo(int x, int y) -+ public static int getGeoX(int worldX) - { -- return hasGeoPos(getGeoX(x), getGeoY(y)); -+ return (MathUtil.limit(worldX, World.MAP_MIN_X, World.MAP_MAX_X) - World.MAP_MIN_X) >> 4; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe check -+ * Converts world Y to geodata Y. -+ * @param worldY -+ * @return int : Geo Y - */ -- public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) -+ public static int getGeoY(int worldY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return true; -- } -- return region.checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(worldY, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.MAP_MIN_Y) >> 4; - } - - /** -+ * Converts geodata X to world X. - * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe anti-corner cut -+ * @return int : World X - */ -- public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) -+ public static int getWorldX(int geoX) - { -- boolean can = true; -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- return can && checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(geoX, 0, GeoStructure.GEO_CELLS_X) << 4) + World.MAP_MIN_X + 8; - } - - /** -- * @param geoX -+ * Converts geodata Y to world Y. - * @param geoY -- * @param worldZ -- * @return the nearest Z value -+ * @return int : World Y - */ -- public int getNearestZ(int geoX, int geoY, int worldZ) -+ public static int getWorldY(int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return worldZ; -- } -- return region.getNearestZ(geoX, geoY, worldZ); -+ return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next lower Z value -+ * Returns block of geodata on given coordinates. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return {@link ABlock} : Block of geodata. - */ -- public int getNextLowerZ(int geoX, int geoY, int worldZ) -+ private final ABlock getBlock(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final int x = geoX / GeoStructure.BLOCK_CELLS_X; -+ final int y = geoY / GeoStructure.BLOCK_CELLS_Y; -+ -+ // if x or y is out of array return null -+ if ((x > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) - { -- return worldZ; -+ return _blocks[x][y]; - } -- return region.getNextLowerZ(geoX, geoY, worldZ); -+ return null; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next higher Z value -+ * Check if geo coordinates has geo. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return boolean : True, if given geo coordinates have geodata - */ -- public int getNextHigherZ(int geoX, int geoY, int worldZ) -+ public boolean hasGeoPos(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final ABlock block = getBlock(geoX, geoY); -+ if (block == null) // null block check - { -- return worldZ; -+ // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) -+ // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); -+ return false; - } -- return region.getNextHigherZ(geoX, geoY, worldZ); -+ return block.hasGeoPos(); - } - - /** -- * Gets the Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the height of cell, which is closest to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, closest to given coordinates. - */ -- public int getHeight(int x, int y, int z) -+ public short getHeightNearest(int geoX, int geoY, int worldZ) - { -- return getNearestZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getHeightNearest(geoX, geoY, worldZ) : (short) worldZ; - } - - /** -- * Gets the next lower Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the NSWE flag byte of cell, which is closes to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. - */ -- public int getLowerHeight(int x, int y, int z) -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) - { -- return getNextLowerZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getNsweNearest(geoX, geoY, worldZ) : (byte) 0xFF; - } - - /** -- * Gets the next higher Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Check if world coordinates has geo. -+ * @param worldX : World X -+ * @param worldY : World Y -+ * @return boolean : True, if given world coordinates have geodata - */ -- public int getHigherHeight(int x, int y, int z) -+ public boolean hasGeo(int worldX, int worldY) - { -- return getNextHigherZ(getGeoX(x), getGeoY(y), z); -+ return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); - } - - /** -- * @param worldX -- * @return the geo X -+ * Returns closest Z coordinate according to geodata. -+ * @param worldX : world x -+ * @param worldY : world y -+ * @param worldZ : world z -+ * @return short : nearest Z coordinates according to geodata - */ -- public int getGeoX(int worldX) -+ public short getHeight(int worldX, int worldY, int worldZ) - { -- return (worldX - WORLD_MIN_X) / 16; -+ return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - -- /** -- * @param worldY -- * @return the geo Y -- */ -- public int getGeoY(int worldY) -- { -- return (worldY - WORLD_MIN_Y) / 16; -- } -+ // PATHFINDING - - /** -- * @param worldZ -- * @return the geo Z -+ * Check line of sight from {@link WorldObject} to {@link WorldObject}. -+ * @param origin : The origin object. -+ * @param target : The target object. -+ * @return {@code boolean} : True if origin can see target - */ -- public int getGeoZ(int worldZ) -+ public boolean canSeeTarget(WorldObject origin, WorldObject target) - { -- return (worldZ - WORLD_MIN_Z) / 16; -- } -- -- /** -- * @param geoX -- * @return the world X -- */ -- public int getWorldX(int geoX) -- { -- return (geoX * 16) + WORLD_MIN_X + 8; -- } -- -- /** -- * @param geoY -- * @return the world Y -- */ -- public int getWorldY(int geoY) -- { -- return (geoY * 16) + WORLD_MIN_Y + 8; -- } -- -- /** -- * @param geoZ -- * @return the world Z -- */ -- public int getWorldZ(int geoZ) -- { -- return (geoZ * 16) + WORLD_MIN_Z + 8; -- } -- -- /** -- * Can see target. Doors as target always return true. Checks doors between. -- * @param cha the character -- * @param target the target -- * @return {@code true} if the character can see the target (LOS), {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, WorldObject target) -- { -- if (target.isDoor()) -+ if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) - { -- // Can always see doors. - return true; - } -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param cha the character -- * @param worldPosition the world position -- * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) -- { -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param world -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @param tworld the target's instanceId -- * @return -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) -- { -- return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instance -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) -- { -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) -+ -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = target.getX(); -+ final int ty = target.getY(); -+ final int tz = target.getZ(); -+ -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } -- return canSeeTarget(x, y, z, tx, ty, tz); -- } -- -- /** -- * @param prevX -- * @param prevY -- * @param prevGeoZ -- * @param curX -- * @param curY -- * @param nswe -- * @return the LOS Z value -- */ -- private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) -- { -- if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { -- throw new RuntimeException("Multiple directions!"); -+ return false; - } - -- if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- return getNearestZ(curX, curY, prevGeoZ); -+ return true; - } - -- return getNextHigherZ(curX, curY, prevGeoZ); -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) -+ { -+ return true; -+ } -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) -+ { -+ return goz == gtz; -+ } -+ -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) -+ { -+ oheight = ((Creature) origin).getCollisionHeight() * 2; -+ } -+ -+ double theight = 0; -+ if (target.isCreature()) -+ { -+ theight = ((Creature) target).getCollisionHeight() * 2; -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); - } - - /** -- * Can see target. Does not check doors between. -- * @param xValue the x coordinate -- * @param yValue the y coordinate -- * @param zValue the z coordinate -- * @param txValue the target's x coordinate -- * @param tyValue the target's y coordinate -- * @param tzValue the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -+ * Check line of sight from {@link WorldObject} to {@link Location}. -+ * @param origin : The origin object. -+ * @param position : The target position. -+ * @return {@code boolean} : True if object can see position - */ -- public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) -+ public boolean canSeeTarget(WorldObject origin, Location position) - { -- int x = xValue; -- int y = yValue; -- int tx = txValue; -- int ty = tyValue; -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = position.getX(); -+ final int ty = position.getY(); -+ final int tz = position.getZ(); - -- int geoX = getGeoX(x); -- int geoY = getGeoY(y); -- int tGeoX = getGeoX(tx); -- int tGeoY = getGeoY(ty); -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) -+ { -+ return false; -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) -+ { -+ return false; -+ } - -- int z = getNearestZ(geoX, geoY, zValue); -- int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- // Fastpath. -- if ((geoX == tGeoX) && (geoY == tGeoY)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- if (hasGeoPos(tGeoX, tGeoY)) -- { -- return z == tz; -- } - return true; - } - -- if (tz > z) -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) - { -- int tmp = tx; -- tx = x; -- x = tmp; -- -- tmp = ty; -- ty = y; -- y = tmp; -- -- tmp = tz; -- tz = z; -- z = tmp; -- -- tmp = tGeoX; -- tGeoX = geoX; -- geoX = tmp; -- -- tmp = tGeoY; -- tGeoY = geoY; -- geoY = tmp; -+ return goz == gtz; - } - -- final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); -- // First point is guaranteed to be available, skip it, we can always see our own position. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- final int prevZ = pointIter.z(); -- int prevGeoZ = prevZ; -- int ptIndex = 0; -- while (pointIter.next()) -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight(); -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); -+ } -+ -+ /** -+ * Simple check for origin to target visibility. -+ * @param goxValue : origin X geodata coordinate -+ * @param goyValue : origin Y geodata coordinate -+ * @param gozValue : origin Z geodata coordinate -+ * @param oheight : origin height (if instance of {@link Character}) -+ * @param gtxValue : target X geodata coordinate -+ * @param gtyValue : target Y geodata coordinate -+ * @param gtzValue : target Z geodata coordinate -+ * @param theight : target height (if instance of {@link Character}) -+ * @param instance -+ * @return {@code boolean} : True, when target can be seen. -+ */ -+ private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) -+ { -+ int goz = gozValue; -+ int gtz = gtzValue; -+ int gox = goxValue; -+ int goy = goyValue; -+ int gtx = gtxValue; -+ int gty = gtyValue; -+ -+ // get line of sight Z coordinates -+ double losoz = goz + ((oheight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ double lostz = gtz + ((theight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ -+ // get X delta and signum -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirox = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; -+ final byte dirtx = sx > 0 ? GeoStructure.CELL_FLAG_W : GeoStructure.CELL_FLAG_E; -+ -+ // get Y delta and signum -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte diroy = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ final byte dirty = sy > 0 ? GeoStructure.CELL_FLAG_N : GeoStructure.CELL_FLAG_S; -+ -+ // get Z delta -+ final int dm = Math.max(dx, dy); -+ final double dz = (lostz - losoz) / dm; -+ -+ // get direction flag for diagonal movement -+ final byte diroxy = getDirXY(dirox, diroy); -+ final byte dirtxy = getDirXY(dirtx, dirty); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte diro; -+ byte dirt; -+ -+ // initialize node values -+ int nox = gox; -+ int noy = goy; -+ int ntx = gtx; -+ int nty = gty; -+ byte nsweo = getNsweNearest(gox, goy, goz); -+ byte nswet = getNsweNearest(gtx, gty, gtz); -+ -+ // loop -+ ABlock block; -+ int index; -+ for (int i = 0; i < ((dm + 1) / 2); i++) -+ { -+ // reset direction flag -+ diro = 0; -+ dirt = 0; - -- if ((curX == prevX) && (curY == prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- continue; -+ // calculate next point XY coordinates -+ d -= dy; -+ d += dx; -+ nox += sx; -+ ntx -= sx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroxy; -+ dirt |= dirtxy; - } -+ else if (e2 > -dy) -+ { -+ // calculate next point X coordinate -+ d -= dy; -+ nox += sx; -+ ntx -= sx; -+ diro |= dirox; -+ dirt |= dirtx; -+ } -+ else if (e2 < dx) -+ { -+ // calculate next point Y coordinate -+ d += dx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroy; -+ dirt |= dirty; -+ } - -- final int beeCurZ = pointIter.z(); -- int curGeoZ = prevGeoZ; -- -- // Check if the position has geodata. -- if (hasGeoPos(curX, curY)) - { -- final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); -- curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); -- int maxHeight; -- if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) -+ // get block of the next cell -+ block = getBlock(nox, noy); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nsweo & diro) == 0) - { -- maxHeight = z + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); - } - else - { -- maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); - } - -- boolean canSeeThrough = false; -- if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) -+ // layer does not exist, return -+ if (index == -1) - { -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else -- { -- canSeeThrough = true; -- } -+ return false; - } - -- if (!canSeeThrough) -+ // get layer and next line of sight Z coordinate -+ goz = block.getHeight(index); -+ losoz += dz; -+ -+ // perform line of sight check, return when fails -+ if ((goz - losoz) > Config.MAX_OBSTACLE_HEIGHT) - { - return false; - } -+ -+ // get layer nswe -+ nsweo = block.getNswe(index); - } -+ { -+ // get block of the next cell -+ block = getBlock(ntx, nty); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nswet & dirt) == 0) -+ { -+ index = block.getIndexAbove(ntx, nty, gtz - GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ else -+ { -+ index = block.getIndexBelow(ntx, nty, gtz + GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ -+ // layer does not exist, return -+ if (index == -1) -+ { -+ return false; -+ } -+ -+ // get layer and next line of sight Z coordinate -+ gtz = block.getHeight(index); -+ lostz -= dz; -+ -+ // perform line of sight check, return when fails -+ if ((gtz - lostz) > Config.MAX_OBSTACLE_HEIGHT) -+ { -+ return false; -+ } -+ -+ // get layer nswe -+ nswet = block.getNswe(index); -+ } - -- prevX = curX; -- prevY = curY; -- prevGeoZ = curGeoZ; -- ++ptIndex; -+ // update coords -+ gox = nox; -+ goy = noy; -+ gtx = ntx; -+ gty = nty; - } - -- return true; -+ // when iteration is completed, compare final Z coordinates -+ return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); - } - - /** -- * Move check. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param zValue the z coordinate -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tzValue the target's z coordinate -- * @param instance the instance -- * @return the last Location (x,y,z) where player can walk - just before wall -+ * Check movement from coordinates to coordinates. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {code boolean} : True if target coordinates are reachable from origin coordinates - */ -- public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) -+ public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- final int geoX = getGeoX(x); -- final int geoY = getGeoY(y); -- final int z = getNearestZ(geoX, geoY, zValue); -- final int tGeoX = getGeoX(tx); -- final int tGeoY = getGeoY(ty); -- final int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } -- if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = z; -+ // perform geodata check -+ final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); -+ return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); -+ } -+ -+ /** -+ * Check movement from origin to target. Returns last available point in the checked path. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {@link Location} : Last point where object can walk (just before wall) -+ */ -+ public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) -+ { -+ return new Location(ox, oy, oz); -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) -+ { -+ return new Location(ox, oy, oz); -+ } - -- while (pointIter.next()) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -- -- if (hasGeoPos(prevX, prevY)) -- { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -- { -- // Can't move, return previous location. -- return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); -- } -- } -- -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return new Location(tx, ty, tz); - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != tz)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- // Different floors, return start location. -- return new Location(x, y, z); -+ return new Location(tx, ty, tz); - } - -- return new Location(tx, ty, tz); -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) -+ { -+ return new Location(tx, ty, tz); -+ } -+ -+ // perform geodata check -+ return checkMove(gox, goy, goz, gtx, gty, gtz, instance); - } - - /** -- * Checks if its possible to move from one location to another. -- * @param fromX the X coordinate to start checking from -- * @param fromY the Y coordinate to start checking from -- * @param fromZvalue the Z coordinate to start checking from -- * @param toX the X coordinate to end checking at -- * @param toY the Y coordinate to end checking at -- * @param toZvalue the Z coordinate to end checking at -- * @param instance the instance -- * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise -+ * With this method you can check if a position is visible or can be reached by beeline movement.
-+ * Target X and Y reachable and Z is on same floor: -+ *
    -+ *
  • Location of the target with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y reachable but Z is on another floor: -+ *
    -+ *
  • Location of the origin with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y not reachable: -+ *
    -+ *
  • Last accessible location in destination to target.
  • -+ *
-+ * @param gox : origin X geodata coordinate -+ * @param goy : origin Y geodata coordinate -+ * @param goz : origin Z geodata coordinate -+ * @param gtx : target X geodata coordinate -+ * @param gty : target Y geodata coordinate -+ * @param gtz : target Z geodata coordinate -+ * @param instance -+ * @return {@link GeoLocation} : The last allowed point of movement. - */ -- public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) -+ protected final GeoLocation checkMove(int gox, int goy, int goz, int gtx, int gty, int gtz, Instance instance) - { -- final int geoX = getGeoX(fromX); -- final int geoY = getGeoY(fromY); -- final int fromZ = getNearestZ(geoX, geoY, fromZvalue); -- final int tGeoX = getGeoX(toX); -- final int tGeoY = getGeoY(toY); -- final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); -- -- if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) -+ if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } -- if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) -+ if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = fromZ; -+ // get X delta, signum and direction flag -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirX = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; - -- while (pointIter.next()) -+ // get Y delta, signum and direction flag -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte dirY = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ -+ // get direction flag for diagonal movement -+ final byte dirXY = getDirXY(dirX, dirY); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte direction; -+ -+ // load pointer coordinates -+ int gpx = gox; -+ int gpy = goy; -+ int gpz = goz; -+ -+ // load next pointer -+ int nx = gpx; -+ int ny = gpy; -+ -+ // loop -+ int count = 0; -+ while (count++ < Config.MAX_ITERATIONS) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -+ direction = 0; - -- if (hasGeoPos(prevX, prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -+ d -= dy; -+ d += dx; -+ nx += sx; -+ ny += sy; -+ direction |= dirXY; -+ } -+ else if (e2 > -dy) -+ { -+ d -= dy; -+ nx += sx; -+ direction |= dirX; -+ } -+ else if (e2 < dx) -+ { -+ d += dx; -+ ny += sy; -+ direction |= dirY; -+ } -+ -+ // obstacle found, return -+ if ((getNsweNearest(gpx, gpy, gpz) & direction) == 0) -+ { -+ return new GeoLocation(gpx, gpy, gpz); -+ } -+ -+ // update pointer coordinates -+ gpx = nx; -+ gpy = ny; -+ gpz = getHeightNearest(nx, ny, gpz); -+ -+ // target coordinates reached -+ if ((gpx == gtx) && (gpy == gty)) -+ { -+ if (gpz == gtz) - { -- return false; -+ // path found, Z coordinates are okay, return target point -+ return new GeoLocation(gtx, gty, gtz); - } -+ -+ // path found, Z coordinates are not okay, return last good point -+ return new GeoLocation(gpx, gpy, gpz); - } -+ } -+ -+ return new GeoLocation(gox, goy, goz); -+ } -+ -+ /** -+ * Returns diagonal NSWE flag format of combined two NSWE flags. -+ * @param dirX : X direction NSWE flag -+ * @param dirY : Y direction NSWE flag -+ * @return byte : NSWE flag of combined direction -+ */ -+ private static byte getDirXY(byte dirX, byte dirY) -+ { -+ // check axis directions -+ if (dirY == GeoStructure.CELL_FLAG_N) -+ { -+ if (dirX == GeoStructure.CELL_FLAG_W) -+ { -+ return GeoStructure.CELL_FLAG_NW; -+ } - -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return GeoStructure.CELL_FLAG_NE; - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) -+ if (dirX == GeoStructure.CELL_FLAG_W) - { -- // Different floors. -- return false; -+ return GeoStructure.CELL_FLAG_SW; - } - -- return true; -+ return GeoStructure.CELL_FLAG_SE; - } - -+ /** -+ * Returns the list of location objects as a result of complete path calculation. -+ * @param ox : origin x -+ * @param oy : origin y -+ * @param oz : origin z -+ * @param tx : target x -+ * @param ty : target y -+ * @param tz : target z -+ * @param instance -+ * @return {@code List} : complete path from nodes -+ */ -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ return null; -+ } -+ -+ /** -+ * Returns the instance of the {@link GeoEngine}. -+ * @return {@link GeoEngine} : The instance. -+ */ - public static GeoEngine getInstance() - { - return SingletonHolder.INSTANCE; -@@ -743,6 +934,6 @@ - - private static class SingletonHolder - { -- protected static final GeoEngine INSTANCE = new GeoEngine(); -+ protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); - } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (working copy) -@@ -20,88 +20,84 @@ - import java.util.LinkedList; - import java.util.List; - import java.util.ListIterator; --import java.util.logging.Level; --import java.util.logging.Logger; - - import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; --import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; --import org.l2jmobius.gameserver.model.World; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -+import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.instancezone.Instance; - - /** -- * @author -Nemesiss- -+ * @author Hasha - */ --public class GeoEnginePathfinding -+final class GeoEnginePathfinding extends GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); -+ // pre-allocated buffers -+ private final BufferHolder[] _buffers; - -- private BufferInfo[] _buffers; -- - protected GeoEnginePathfinding() - { -- try -+ super(); -+ -+ final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ _buffers = new BufferHolder[array.length]; -+ int count = 0; -+ for (int i = 0; i < array.length; i++) - { -- final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ final String buf = array[i]; -+ final String[] args = buf.split("x"); - -- _buffers = new BufferInfo[array.length]; -- -- String buf; -- String[] args; -- for (int i = 0; i < array.length; i++) -+ try - { -- buf = array[i]; -- args = buf.split("x"); -- if (args.length != 2) -- { -- throw new Exception("Invalid buffer definition: " + buf); -- } -- -- _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); -+ final int size = Integer.parseInt(args[1]); -+ count += size; -+ _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); - } -+ catch (Exception e) -+ { -+ LOGGER.warning("GeoEnginePathfinding: Can not load buffer setting: " + buf); -+ } - } -- catch (Exception e) -- { -- LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); -- throw new Error("CellPathFinding: load aborted"); -- } -+ -+ LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); - } - -- public boolean pathNodesExist(short regionoffset) -+ @Override -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- return false; -- } -- -- public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) -- { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -- if (!GeoEngine.getInstance().hasGeo(x, y)) -+ // get origin and check existing geo coords -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { - 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)) -+ -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coords -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { - 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)))); -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // Prepare buffer for pathfinding calculations -+ final NodeBuffer buffer = getBuffer(64 + (2 * Math.max(Math.abs(gox - gtx), Math.abs(goy - gty)))); - if (buffer == null) - { - return null; - } - -- List path = null; -+ // find path -+ List path = null; - try - { -- final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); -- -+ final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); - if (result == null) - { - return null; -@@ -125,33 +121,45 @@ - return path; - } - -- int currentX, currentY, currentZ; -- ListIterator middlePoint; -+ // get path list iterator -+ final ListIterator point = path.listIterator(); - -- middlePoint = path.listIterator(); -- currentX = x; -- currentY = y; -- currentZ = z; -+ // get node A (origin) -+ int nodeAx = gox; -+ int nodeAy = goy; -+ short nodeAz = goz; - -- while (middlePoint.hasNext()) -+ // get node B -+ GeoLocation nodeB = (GeoLocation) point.next(); -+ -+ // iterate thought the path to optimize it -+ int count = 0; -+ while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) - { -- final AbstractNodeLoc locMiddle = middlePoint.next(); -- if (!middlePoint.hasNext()) -- { -- break; -- } -+ // get node C -+ final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - -- final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); -- if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) -+ // check movement from node A to node C -+ final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); -+ if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) - { -- middlePoint.remove(); -+ // can move from node A to node C -+ -+ // remove node B -+ point.remove(); - } - else - { -- currentX = locMiddle.getX(); -- currentY = locMiddle.getY(); -- currentZ = locMiddle.getZ(); -+ // can not move from node A to node C -+ -+ // set node A (node B is part of path, update A coordinates) -+ nodeAx = nodeB.getGeoX(); -+ nodeAy = nodeB.getGeoY(); -+ nodeAz = (short) nodeB.getZ(); - } -+ -+ // set node B -+ nodeB = (GeoLocation) point.next(); - } - - return path; -@@ -158,144 +166,102 @@ - } - - /** -- * Convert geodata position to pathnode position -- * @param geo_pos -- * @return pathnode position -+ * Create list of node locations as result of calculated buffer node tree. -+ * @param node : the entry point -+ * @return List : list of node location - */ -- public short getNodePos(int geo_pos) -+ private static List constructPath(Node node) - { -- return (short) (geo_pos >> 3); // OK? -- } -- -- /** -- * Convert node position to pathnode block position -- * @param node_pos -- * @return pathnode block position (0...255) -- */ -- public short getNodeBlock(int node_pos) -- { -- return (short) (node_pos % 256); -- } -- -- public byte getRegionX(int node_pos) -- { -- return (byte) ((node_pos >> 8) + World.TILE_X_MIN); -- } -- -- public byte getRegionY(int node_pos) -- { -- return (byte) ((node_pos >> 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 node_x rx -- * @return -- */ -- public int calculateWorldX(short node_x) -- { -- return World.MAP_MIN_X + (node_x * 128) + 48; -- } -- -- /** -- * Convert pathnode y to World y position -- * @param node_y -- * @return -- */ -- public int calculateWorldY(short node_y) -- { -- return World.MAP_MIN_Y + (node_y * 128) + 48; -- } -- -- private List constructPath(AbstractNode nodeValue) -- { -- final LinkedList path = new LinkedList<>(); -- int previousDirectionX = Integer.MIN_VALUE; -- int previousDirectionY = Integer.MIN_VALUE; -- int directionX, directionY; -+ // create empty list -+ final LinkedList list = new LinkedList<>(); - -- AbstractNode node = nodeValue; -- while (node.getParent() != null) -+ // set direction X/Y -+ int dx = 0; -+ int dy = 0; -+ -+ // get target parent -+ Node target = node; -+ Node parent = target.getParent(); -+ -+ // while parent exists -+ int count = 0; -+ while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { -- directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); -- directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); -+ // get parent <> target direction X/Y -+ final int nx = parent.getLoc().getGeoX() - target.getLoc().getGeoX(); -+ final int ny = parent.getLoc().getGeoY() - target.getLoc().getGeoY(); - -- // only add a new route point if moving direction changes -- if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) -+ // direction has changed? -+ if ((dx != nx) || (dy != ny)) - { -- previousDirectionX = directionX; -- previousDirectionY = directionY; -+ // add node to the beginning of the list -+ list.addFirst(target.getLoc()); - -- path.addFirst(node.getLoc()); -- node.setLoc(null); -+ // update direction X/Y -+ dx = nx; -+ dy = ny; - } - -- node = node.getParent(); -+ // move to next node, set target and get its parent -+ target = parent; -+ parent = target.getParent(); - } - -- return path; -+ // return list -+ return list; - } - -- private CellNodeBuffer alloc(int size) -+ /** -+ * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer. -+ * @param size : pre-calculated minimal required size -+ * @return NodeBuffer : buffer -+ */ -+ private final NodeBuffer getBuffer(int size) - { -- CellNodeBuffer current = null; -- for (BufferInfo i : _buffers) -+ NodeBuffer current = null; -+ for (BufferHolder holder : _buffers) - { -- if (i.mapSize >= size) -+ // Find proper size of buffer -+ if (holder._size < size) - { -- for (CellNodeBuffer buf : i.buffer) -+ continue; -+ } -+ -+ // Find unlocked NodeBuffer -+ for (NodeBuffer buffer : holder._buffer) -+ { -+ if (!buffer.isLocked()) - { -- if (buf.lock()) -- { -- current = buf; -- break; -- } -+ continue; - } -- if (current != null) -- { -- break; -- } - -- // not found, allocate temporary buffer -- current = new CellNodeBuffer(i.mapSize); -- current.lock(); -- if (i.buffer.size() < i.count) -- { -- i.buffer.add(current); -- break; -- } -+ return buffer; - } -+ -+ // NodeBuffer not found, allocate temporary buffer -+ current = new NodeBuffer(holder._size); -+ current.isLocked(); - } - - return current; - } - -- private static final class BufferInfo -+ /** -+ * NodeBuffer container with specified size and count of separate buffers. -+ */ -+ private static final class BufferHolder - { -- final int mapSize; -- final int count; -- ArrayList buffer; -+ final int _size; -+ List _buffer; - -- public BufferInfo(int size, int cnt) -+ public BufferHolder(int size, int count) - { -- mapSize = size; -- count = cnt; -- buffer = new ArrayList<>(count); -+ _size = size; -+ _buffer = new ArrayList<>(count); -+ for (int i = 0; i < count; i++) -+ { -+ _buffer.add(new NodeBuffer(size)); -+ } - } - } -- -- public static GeoEnginePathfinding getInstance() -- { -- return SingletonHolder.INSTANCE; -- } -- -- private static class SingletonHolder -- { -- protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); -- } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (working copy) -@@ -0,0 +1,197 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @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 height of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getHeightNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first above given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, above given coordinates. -+ */ -+ public abstract short getHeightAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first below given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is closest to given coordinates. -+ * @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 the NSWE flag byte of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getNsweNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first above given coordinates. -+ * @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 getNsweAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first below given coordinates. -+ * @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 getNsweBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is closes layer to given coordinates. -+ * @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. -+ * @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 above given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is first layer below given coordinates. -+ * @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 index to data of the cell, which is first layer below given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexBelowOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeight(int index); -+ -+ /** -+ * Returns the height of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightOriginal(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNswe(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNsweOriginal(int index); -+ -+ /** -+ * Sets the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @param nswe : New NSWE flag byte. -+ */ -+ public abstract void setNswe(int index, byte nswe); -+ -+ /** -+ * Saves the block in L2D format to {@link BufferedOutputStream}. Used only for L2D geodata conversion. -+ * @param stream : The stream. -+ * @throws IOException : Can't save the block to steam. -+ */ -+ public abstract void saveBlock(BufferedOutputStream stream) throws IOException; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (working copy) -@@ -0,0 +1,252 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockComplex(ByteBuffer bb, GeoFormat format) -+ { -+ // initialize buffer -+ _buffer = new byte[GeoStructure.BLOCK_CELLS * 3]; -+ -+ // load data -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // 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); -+ } -+ else -+ { -+ // get nswe -+ final byte nswe = bb.get(); -+ _buffer[i * 3] = nswe; -+ -+ // get height -+ final short height = bb.getShort(); -+ _buffer[(i * 3) + 1] = (byte) (height & 0x00FF); -+ _buffer[(i * 3) + 2] = (byte) (height >> 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height > worldZ ? height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height < worldZ ? height : Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public 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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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 int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_COMPLEX_L2D); -+ -+ // write block data -+ stream.write(_buffer, 0, GeoStructure.BLOCK_CELLS * 3); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (working copy) -@@ -0,0 +1,176 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockFlat extends ABlock -+{ -+ protected final short _height; -+ protected byte _nswe; -+ -+ /** -+ * Creates FlatBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockFlat(ByteBuffer bb, GeoFormat format) -+ { -+ _height = bb.getShort(); -+ _nswe = format != GeoFormat.L2D ? 0x0F : (byte) (0xFF); -+ if (format == GeoFormat.L2OFF) -+ { -+ bb.getShort(); -+ } -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return true; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height > worldZ ? _height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height < worldZ ? _height : Short.MAX_VALUE; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height > worldZ ? _nswe : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height < worldZ ? _nswe : 0; -+ } -+ -+ @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return index -+ return _height < worldZ ? 0 : -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _nswe = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_FLAT_L2D); -+ -+ // write height -+ stream.write((byte) (_height & 0x00FF)); -+ stream.write((byte) (_height >> 8)); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (working copy) -@@ -0,0 +1,465 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+import java.nio.ByteOrder; -+import java.util.Arrays; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockMultilayer extends ABlock -+{ -+ private static final int MAX_LAYERS = Byte.MAX_VALUE; -+ -+ private static ByteBuffer _temp; -+ -+ /** -+ * Initializes the temporarily buffer. -+ */ -+ public static void initialize() -+ { -+ // initialize temporarily buffer and sorting mechanism -+ _temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3); -+ _temp.order(ByteOrder.LITTLE_ENDIAN); -+ } -+ -+ /** -+ * Releases temporarily buffer. -+ */ -+ public static void release() -+ { -+ _temp = null; -+ } -+ -+ protected byte[] _buffer; -+ -+ /** -+ * Implicit constructor for children class. -+ */ -+ protected BlockMultilayer() -+ { -+ _buffer = null; -+ } -+ -+ /** -+ * Creates MultilayerBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockMultilayer(ByteBuffer bb, GeoFormat format) -+ { -+ // 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 = format != GeoFormat.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++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // get data -+ final short data = bb.getShort(); -+ -+ // add nswe and height -+ _temp.put((byte) (data & 0x000F)); -+ _temp.putShort((short) ((short) (data & 0xFFF0) >> 1)); -+ } -+ else -+ { -+ // add nswe -+ _temp.put(bb.get()); -+ -+ // add height -+ _temp.putShort(bb.getShort()); -+ } -+ } -+ } -+ -+ // 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 height -+ if (height > worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, return minimum value -+ return Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 height -+ if (height < worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, return maximum value -+ return Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 nswe -+ if (height > worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 nswe -+ if (height < worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @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 first layer data (first from bottom) -+ byte layers = _buffer[index++]; -+ -+ // loop though all cell layers, find closest layer -+ 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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ // get height -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(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]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ // get nswe -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ // set nswe -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_MULTILAYER_L2D); -+ -+ // for each cell -+ int index = 0; -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ // write layers count -+ final byte layers = _buffer[index++]; -+ stream.write(layers); -+ -+ // write cell data -+ stream.write(_buffer, index, layers * 3); -+ -+ // move index to next cell -+ index += layers * 3; -+ } -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (working copy) -@@ -0,0 +1,150 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockNull extends ABlock -+{ -+ private final byte _nswe; -+ -+ public BlockNull() -+ { -+ _nswe = (byte) 0xFF; -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return false; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweBelow(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) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) -+ { -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (nonexistent) -@@ -1,48 +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 . -- */ --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() -- { -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (nonexistent) -@@ -1,77 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (nonexistent) -@@ -1,56 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (working copy) -@@ -0,0 +1,39 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public enum GeoFormat -+{ -+ L2J("%d_%d.l2j"), -+ L2OFF("%d_%d_conv.dat"), -+ L2D("%d_%d.l2d"); -+ -+ private final String _filename; -+ -+ private GeoFormat(String filename) -+ { -+ _filename = filename; -+ } -+ -+ public String getFilename() -+ { -+ return _filename; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (working copy) -@@ -0,0 +1,67 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.geoengine.GeoEngine; -+import org.l2jmobius.gameserver.model.Location; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoLocation extends Location -+{ -+ private byte _nswe; -+ -+ public GeoLocation(int x, int y, int z) -+ { -+ super(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public void set(int x, int y, short z) -+ { -+ super.setXYZ(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public int getGeoX() -+ { -+ return _x; -+ } -+ -+ public int getGeoY() -+ { -+ return _y; -+ } -+ -+ @Override -+ public int getX() -+ { -+ return GeoEngine.getWorldX(_x); -+ } -+ -+ @Override -+ public int getY() -+ { -+ return GeoEngine.getWorldY(_y); -+ } -+ -+ public byte getNSWE() -+ { -+ return _nswe; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (working copy) -@@ -0,0 +1,70 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoStructure -+{ -+ // cells -+ public static final byte CELL_FLAG_E = 1 << 0; -+ public static final byte CELL_FLAG_W = 1 << 1; -+ public static final byte CELL_FLAG_S = 1 << 2; -+ public static final byte CELL_FLAG_N = 1 << 3; -+ public static final byte CELL_FLAG_SE = 1 << 4; -+ public static final byte CELL_FLAG_SW = 1 << 5; -+ public static final byte CELL_FLAG_NE = 1 << 6; -+ public static final byte CELL_FLAG_NW = (byte) (1 << 7); -+ -+ public static final int CELL_HEIGHT = 8; -+ public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6; -+ -+ // blocks -+ public static final byte TYPE_FLAT_L2J_L2OFF = 0; -+ public static final byte TYPE_FLAT_L2D = (byte) 0xD0; -+ public static final byte TYPE_COMPLEX_L2J = 1; -+ public static final byte TYPE_COMPLEX_L2OFF = 0x40; -+ public static final byte TYPE_COMPLEX_L2D = (byte) 0xD1; -+ 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) -+ public static final byte TYPE_MULTILAYER_L2D = (byte) 0xD2; -+ -+ 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; -+ -+ // regions -+ 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; -+ -+ // global geodata -+ private static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); -+ private 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; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (nonexistent) -@@ -1,42 +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 . -- */ --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); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (working copy) -@@ -0,0 +1,53 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public interface IGeoObject -+{ -+ /** -+ * Returns geodata X coordinate of the {@link IGeoObject}. -+ * @return int : Geodata X coordinate. -+ */ -+ int getGeoX(); -+ -+ /** -+ * Returns geodata Y coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Y coordinate. -+ */ -+ int getGeoY(); -+ -+ /** -+ * Returns geodata Z coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Z coordinate. -+ */ -+ int getGeoZ(); -+ -+ /** -+ * Returns height of the {@link IGeoObject}. -+ * @return int : Height. -+ */ -+ int getHeight(); -+ -+ /** -+ * Returns {@link IGeoObject} data. -+ * @return byte[][] : {@link IGeoObject} data. -+ */ -+ byte[][] getObjectGeoData(); -+} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (nonexistent) -@@ -1,47 +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 . -- */ --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(); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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); -- -- // 1 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (nonexistent) -@@ -1,55 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --/** -- * @author HorridoJoho -- */ --public final class NullRegion implements IRegion --{ -- public static final NullRegion INSTANCE = new NullRegion(); -- -- @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 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Region.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (nonexistent) -@@ -1,92 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (nonexistent) -@@ -1,87 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public abstract class AbstractNode --{ -- private T _loc; -- private AbstractNode _parent; -- -- public AbstractNode(T loc) -- { -- _loc = loc; -- } -- -- public void setParent(AbstractNode p) -- { -- _parent = p; -- } -- -- public AbstractNode getParent() -- { -- return _parent; -- } -- -- public T getLoc() -- { -- return _loc; -- } -- -- public void setLoc(T l) -- { -- _loc = l; -- } -- -- @Override -- public int hashCode() -- { -- final int prime = 31; -- int result = 1; -- result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); -- return result; -- } -- -- @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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (nonexistent) -@@ -1,33 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --/** -- * @author -Nemesiss- -- */ --public abstract class AbstractNodeLoc --{ -- public abstract int getX(); -- -- public abstract int getY(); -- -- public abstract int getZ(); -- -- public abstract int getNodeX(); -- -- public abstract int getNodeY(); --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (nonexistent) -@@ -1,67 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public class CellNode extends AbstractNode --{ -- 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (nonexistent) -@@ -1,348 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --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 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) -- { -- _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(); -- } -- -- public final List debugPath() -- { -- final List 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 void getNeighbors() -- { -- if (!_current.getLoc().canGoAll()) -- { -- 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); -- } -- -- // SouthEast -- if ((nodeE != null) && (nodeS != null)) -- { -- if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) -- { -- addNode(x + 1, y + 1, z, true); -- } -- } -- -- // SouthWest -- if ((nodeS != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) -- { -- addNode(x - 1, y + 1, z, true); -- } -- } -- -- // NorthEast -- if ((nodeN != null) && (nodeE != null)) -- { -- if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) -- { -- addNode(x + 1, y - 1, z, true); -- } -- } -- -- // NorthWest -- if ((nodeN != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) -- { -- addNode(x - 1, y - 1, z, true); -- } -- } -- } -- -- private 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(); -- // reinit node if needed -- if (result.getLoc() != null) -- { -- result.getLoc().set(x, y, z); -- } -- else -- { -- result.setLoc(new NodeLoc(x, y, z)); -- } -- } -- -- return result; -- } -- -- private 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 boolean isHighWeight(int x, int y, int z) -- { -- final CellNode result = getNode(x, y, z); -- if (result == null) -- { -- return true; -- } -- -- if (!result.getLoc().canGoAll()) -- { -- return true; -- } -- if (Math.abs(result.getLoc().getZ() - z) > 16) -- { -- return true; -- } -- -- return false; -- } -- -- private 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (working copy) -@@ -0,0 +1,87 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+ -+/** -+ * @author Hasha -+ */ -+public class Node -+{ -+ // node coords and nswe flag -+ private GeoLocation _loc; -+ -+ // node parent (for reverse path construction) -+ private Node _parent; -+ // node child (for moving over nodes during iteration) -+ private Node _child; -+ -+ // node G cost (movement cost = parent movement cost + current movement cost) -+ private double _cost = -1000; -+ -+ public void setLoc(int x, int y, int z) -+ { -+ _loc = new GeoLocation(x, y, z); -+ } -+ -+ public GeoLocation getLoc() -+ { -+ return _loc; -+ } -+ -+ public void setParent(Node parent) -+ { -+ _parent = parent; -+ } -+ -+ public Node getParent() -+ { -+ return _parent; -+ } -+ -+ public void setChild(Node child) -+ { -+ _child = child; -+ } -+ -+ public Node getChild() -+ { -+ return _child; -+ } -+ -+ public void setCost(double cost) -+ { -+ _cost = cost; -+ } -+ -+ public double getCost() -+ { -+ return _cost; -+ } -+ -+ public void free() -+ { -+ // reset node location -+ _loc = null; -+ -+ // reset node parent, child and cost -+ _parent = null; -+ _child = null; -+ _cost = -1000; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (working copy) -@@ -0,0 +1,306 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import java.util.concurrent.locks.ReentrantLock; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+ -+/** -+ * @author DS, Hasha; Credits to Diamond -+ */ -+public class NodeBuffer -+{ -+ private final ReentrantLock _lock = new ReentrantLock(); -+ private final int _size; -+ private final Node[][] _buffer; -+ -+ // center coordinates -+ private int _cx = 0; -+ private int _cy = 0; -+ -+ // target coordinates -+ private int _gtx = 0; -+ private int _gty = 0; -+ private short _gtz = 0; -+ -+ private Node _current = null; -+ -+ /** -+ * Constructor of NodeBuffer. -+ * @param size : one dimension size of buffer -+ */ -+ public NodeBuffer(int size) -+ { -+ // set size -+ _size = size; -+ -+ // initialize buffer -+ _buffer = new Node[size][size]; -+ for (int x = 0; x < size; x++) -+ { -+ for (int y = 0; y < size; y++) -+ { -+ _buffer[x][y] = 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 Node : first node of path -+ */ -+ public Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) -+ { -+ // set coordinates (middle of the line (gox,goy) - (gtx,gty), will be in the center of the buffer) -+ _cx = gox + ((gtx - gox - _size) / 2); -+ _cy = goy + ((gty - goy - _size) / 2); -+ -+ _gtx = gtx; -+ _gty = gty; -+ _gtz = gtz; -+ -+ _current = getNode(gox, goy, goz); -+ _current.setCost(getCostH(gox, goy, goz)); -+ -+ int count = 0; -+ do -+ { -+ // reached target? -+ if ((_current.getLoc().getGeoX() == _gtx) && (_current.getLoc().getGeoY() == _gty) && (Math.abs(_current.getLoc().getZ() - _gtz) < 8)) -+ { -+ return _current; -+ } -+ -+ // expand current node -+ expand(); -+ -+ // move pointer -+ _current = _current.getChild(); -+ } -+ while ((_current != null) && (count++ < Config.MAX_ITERATIONS)); -+ -+ return null; -+ } -+ -+ public boolean isLocked() -+ { -+ return _lock.tryLock(); -+ } -+ -+ public void free() -+ { -+ _current = null; -+ -+ for (Node[] nodes : _buffer) -+ { -+ for (Node node : nodes) -+ { -+ if (node.getLoc() != null) -+ { -+ node.free(); -+ } -+ } -+ } -+ -+ _lock.unlock(); -+ } -+ -+ /** -+ * Check _current Node and add its neighbors to the buffer. -+ */ -+ private final void expand() -+ { -+ // can't move anywhere, don't expand -+ final byte nswe = _current.getLoc().getNSWE(); -+ if (nswe == 0) -+ { -+ return; -+ } -+ -+ // get geo coords of the node to be expanded -+ final int x = _current.getLoc().getGeoX(); -+ final int y = _current.getLoc().getGeoY(); -+ final short z = (short) _current.getLoc().getZ(); -+ -+ // can move north, expand -+ if ((nswe & GeoStructure.CELL_FLAG_N) != 0) -+ { -+ addNode(x, y - 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move south, expand -+ if ((nswe & GeoStructure.CELL_FLAG_S) != 0) -+ { -+ addNode(x, y + 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_W) != 0) -+ { -+ addNode(x - 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_E) != 0) -+ { -+ addNode(x + 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move north-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NW) != 0) -+ { -+ addNode(x - 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move north-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NE) != 0) -+ { -+ addNode(x + 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SW) != 0) -+ { -+ addNode(x - 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SE) != 0) -+ { -+ addNode(x + 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ } -+ -+ /** -+ * Returns node, if it exists in buffer. -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param z : node Z coord -+ * @return Node : node, if exits in buffer -+ */ -+ private final Node getNode(int x, int y, short z) -+ { -+ // check node X out of coordinates -+ final int ix = x - _cx; -+ if ((ix < 0) || (ix >= _size)) -+ { -+ return null; -+ } -+ -+ // check node Y out of coordinates -+ final int iy = y - _cy; -+ if ((iy < 0) || (iy >= _size)) -+ { -+ return null; -+ } -+ -+ // get node -+ final Node result = _buffer[ix][iy]; -+ -+ // check and update -+ if (result.getLoc() == null) -+ { -+ result.setLoc(x, y, z); -+ } -+ -+ // return node -+ return result; -+ } -+ -+ /** -+ * Add node given by coordinates to the buffer. -+ * @param x : geo X coord -+ * @param y : geo Y coord -+ * @param z : geo Z coord -+ * @param weight : weight of movement to new node -+ */ -+ private final void addNode(int x, int y, short z, int weight) -+ { -+ // get node to be expanded -+ final Node node = getNode(x, y, z); -+ if (node == null) -+ { -+ return; -+ } -+ -+ // Z distance between nearby cells is higher than cell size -+ if (node.getLoc().getZ() > (z + (2 * GeoStructure.CELL_HEIGHT))) -+ { -+ return; -+ } -+ -+ // node was already expanded, return -+ if (node.getCost() >= 0) -+ { -+ return; -+ } -+ -+ node.setParent(_current); -+ if (node.getLoc().getNSWE() != (byte) 0xFF) -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + (weight * Config.OBSTACLE_MULTIPLIER)); -+ } -+ else -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + weight); -+ } -+ -+ Node current = _current; -+ int count = 0; -+ while ((current.getChild() != null) && (count < (Config.MAX_ITERATIONS * 4))) -+ { -+ count++; -+ if (current.getChild().getCost() > node.getCost()) -+ { -+ node.setChild(current.getChild()); -+ break; -+ } -+ current = current.getChild(); -+ } -+ -+ if (count >= (Config.MAX_ITERATIONS * 4)) -+ { -+ System.err.println("Pathfinding: too long loop detected, cost:" + node.getCost()); -+ } -+ -+ current.setChild(node); -+ } -+ -+ /** -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param i : node Z coord -+ * @return double : node cost -+ */ -+ private final double getCostH(int x, int y, int i) -+ { -+ final int dX = x - _gtx; -+ final int dY = y - _gty; -+ final int dZ = (i - _gtz) / GeoStructure.CELL_HEIGHT; -+ -+ // return (Math.abs(dX) + Math.abs(dY) + Math.abs(dZ)) * Config.HEURISTIC_WEIGHT; // Manhattan distance -+ return Math.sqrt((dX * dX) + (dY * dY) + (dZ * dZ)) * Config.HEURISTIC_WEIGHT; // Direct distance -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -- --/** -- * @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 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 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; -- // return super.hashCode(); -- } -- -- @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; -- if (_x != other._x) -- { -- return false; -- } -- if (_y != other._y) -- { -- return false; -- } -- if (_goNorth != other._goNorth) -- { -- return false; -- } -- if (_goEast != other._goEast) -- { -- return false; -- } -- if (_goSouth != other._goSouth) -- { -- return false; -- } -- if (_goWest != other._goWest) -- { -- return false; -- } -- if (_geoHeight != other._geoHeight) -- { -- return false; -- } -- return true; -- } --} -Index: java/org/l2jmobius/gameserver/model/actor/Creature.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/Creature.java (revision 8428) -+++ java/org/l2jmobius/gameserver/model/actor/Creature.java (working copy) -@@ -65,8 +65,6 @@ - import org.l2jmobius.gameserver.enums.TeleportWhereType; - import org.l2jmobius.gameserver.enums.UserInfoType; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; - import org.l2jmobius.gameserver.instancemanager.IdManager; - import org.l2jmobius.gameserver.instancemanager.MapRegionManager; - import org.l2jmobius.gameserver.instancemanager.QuestManager; -@@ -2566,7 +2564,7 @@ - - public boolean disregardingGeodata; - public int onGeodataPathIndex; -- public List geoPath; -+ public List geoPath; - public int geoPathAccurateTx; - public int geoPathAccurateTy; - public int geoPathGtx; -@@ -3443,7 +3441,7 @@ - if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) - { - // Path calculation -- overrides previous movement check -- m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); -+ m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); - if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found - { - if (isPlayer() && !_isFlying && !isInWater) -Index: java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (revision 8424) -+++ java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (working copy) -@@ -12746,7 +12746,7 @@ - { - if (Config.CORRECT_PLAYER_Z) - { -- final int nearestZ = GeoEngine.getInstance().getHigherHeight(getX(), getY(), getZ()); -+ final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); - if (getZ() < nearestZ) - { - teleToLocation(new Location(getX(), getY(), nearestZ)); -Index: java/org/l2jmobius/gameserver/util/GeoUtils.java -=================================================================== ---- java/org/l2jmobius/gameserver/util/GeoUtils.java (revision 8409) -+++ java/org/l2jmobius/gameserver/util/GeoUtils.java (working copy) -@@ -19,7 +19,7 @@ - import java.awt.Color; - - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; - import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; - -@@ -26,25 +26,25 @@ - /** - * @author HorridoJoho - */ --public final class GeoUtils -+public class GeoUtils - { - public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); - - final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); - - while (iter.next()) - { -- final int wx = GeoEngine.getInstance().getWorldX(iter.x()); -- final int wy = GeoEngine.getInstance().getWorldY(iter.y()); -+ final int wx = GeoEngine.getWorldX(iter.x()); -+ final int wy = GeoEngine.getWorldY(iter.y()); - - prim.addPoint(Color.RED, wx, wy, z); - } -@@ -53,21 +53,21 @@ - - public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); -- int wy = GeoEngine.getInstance().getWorldY(prevY); -+ int wx = GeoEngine.getWorldX(prevX); -+ int wy = GeoEngine.getWorldY(prevY); - int wz = iter.z(); - prim.addPoint(Color.RED, wx, wy, wz); - -@@ -78,8 +78,8 @@ - - if ((curX != prevX) || (curY != prevY)) - { -- wx = GeoEngine.getInstance().getWorldX(curX); -- wy = GeoEngine.getInstance().getWorldY(curY); -+ wx = GeoEngine.getWorldX(curX); -+ wy = GeoEngine.getWorldY(curY); - wz = iter.z(); - - prim.addPoint(Color.RED, wx, wy, wz); -@@ -93,7 +93,7 @@ - - private static Color getDirectionColor(int x, int y, int z, int nswe) - { -- if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) -+ if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) - { - return Color.GREEN; - } -@@ -109,9 +109,8 @@ - int iPacket = 0; - - ExServerPrimitive exsp = null; -- final GeoEngine ge = GeoEngine.getInstance(); -- final int playerGx = ge.getGeoX(player.getX()); -- final int playerGy = ge.getGeoY(player.getY()); -+ final int playerGx = GeoEngine.getGeoX(player.getX()); -+ final int playerGy = GeoEngine.getGeoY(player.getY()); - for (int dx = -geoRadius; dx <= geoRadius; ++dx) - { - for (int dy = -geoRadius; dy <= geoRadius; ++dy) -@@ -135,12 +134,12 @@ - final int gx = playerGx + dx; - final int gy = playerGy + dy; - -- final int x = ge.getWorldX(gx); -- final int y = ge.getWorldY(gy); -- final int z = ge.getNearestZ(gx, gy, player.getZ()); -+ final int x = GeoEngine.getWorldX(gx); -+ final int y = GeoEngine.getWorldY(gy); -+ final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); - - // north arrow -- Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); -+ Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); - 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); -@@ -147,7 +146,7 @@ - exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z); - - // east arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); - 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); -@@ -154,13 +153,13 @@ - exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z); - - // south arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); - 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, Cell.NSWE_WEST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); - 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); -@@ -188,15 +187,15 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_EAST; -+ return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_EAST; -+ return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; - } - else - { -- return Cell.NSWE_EAST; -+ return GeoStructure.CELL_FLAG_E; // Direction.EAST; - } - } - else if (x < lastX) // west -@@ -203,26 +202,27 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_WEST; -+ return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_WEST; -+ return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; - } - else - { -- return Cell.NSWE_WEST; -+ return GeoStructure.CELL_FLAG_W; // Direction.WEST; - } - } -- else // unchanged x -+ else -+ // unchanged x - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH; -+ return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH; -+ return GeoStructure.CELL_FLAG_N; // Direction.NORTH; - } - else - { -Index: java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java -=================================================================== ---- java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (nonexistent) -+++ java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (working copy) -@@ -0,0 +1,386 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.tools.geodataconverter; -+ -+import java.io.BufferedOutputStream; -+import java.io.File; -+import java.io.FileOutputStream; -+import java.io.RandomAccessFile; -+import java.nio.ByteOrder; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.Scanner; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.commons.util.PropertiesParser; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoDataConverter -+{ -+ private static GeoFormat _format; -+ private static ABlock[][] _blocks; -+ -+ public static void main(String[] args) -+ { -+ // initialize config -+ loadGeoengineConfigs(); -+ -+ // get geodata format -+ String type = ""; -+ try (Scanner scn = new Scanner(System.in)) -+ { -+ while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) -+ { -+ System.out.println("GeoDataConverter: Select source geodata type:"); -+ System.out.println(" J: L2J (e.g. 23_22.l2j)"); -+ System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); -+ System.out.println(" E: Exit"); -+ System.out.print("Choice: "); -+ type = scn.next(); -+ } -+ } -+ if (type.equalsIgnoreCase("E")) -+ { -+ System.exit(0); -+ } -+ -+ _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; -+ -+ // start conversion -+ System.out.println("GeoDataConverter: Converting all " + _format + " files."); -+ -+ // initialize geodata container -+ _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files -+ int converted = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) -+ { -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) -+ { -+ final String input = String.format(_format.getFilename(), rx, ry); -+ final String filepath = Config.GEODATA_PATH; -+ final File f = new File(filepath + input); -+ if (f.exists() && !f.isDirectory()) -+ { -+ // load geodata -+ if (!loadGeoBlocks(input)) -+ { -+ System.out.println("GeoDataConverter: Unable to load " + input + " region file."); -+ continue; -+ } -+ -+ // recalculate nswe -+ if (!recalculateNswe()) -+ { -+ System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); -+ continue; -+ } -+ -+ // save geodata -+ final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); -+ if (!saveGeoBlocks(output)) -+ { -+ System.out.println("GeoDataConverter: Unable to save " + output + " region file."); -+ continue; -+ } -+ -+ converted++; -+ System.out.println("GeoDataConverter: Created " + output + " region file."); -+ } -+ } -+ } -+ System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); -+ } -+ -+ /** -+ * Loads geo blocks from buffer of the region file. -+ * @param filename : The name of the to load. -+ * @return boolean : True when successful. -+ */ -+ private static boolean loadGeoBlocks(String filename) -+ { -+ // region file is load-able, try to load it -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) -+ { -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) -+ if (_format == GeoFormat.L2OFF) -+ { -+ for (int i = 0; i < 18; i++) -+ { -+ buffer.get(); -+ } -+ } -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ if (_format == GeoFormat.L2J) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2J: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2J: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ else -+ { -+ // get block type -+ final short type = buffer.getShort(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ default: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ if (buffer.remaining() > 0) -+ { -+ System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ return false; -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); -+ return false; -+ } -+ } -+ -+ /** -+ * Recalculate diagonal flags for the region file. -+ * @return boolean : True when successful. -+ */ -+ private static boolean recalculateNswe() -+ { -+ try -+ { -+ for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) -+ { -+ for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) -+ { -+ // get block -+ final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // skip flat blocks -+ if (block instanceof BlockFlat) -+ { -+ continue; -+ } -+ -+ // for complex and multilayer blocks go though all layers -+ short height = Short.MAX_VALUE; -+ int index; -+ while ((index = block.getIndexBelow(x, y, height)) != -1) -+ { -+ // get height and nswe -+ height = block.getHeight(index); -+ byte nswe = block.getNswe(index); -+ -+ // update nswe with diagonal flags -+ nswe = updateNsweBelow(x, y, height, nswe); -+ -+ // set nswe of the cell -+ block.setNswe(index, nswe); -+ } -+ } -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ /** -+ * Updates the NSWE flag with diagonal flags. -+ * @param x : Geodata X coordinate. -+ * @param y : Geodata Y coordinate. -+ * @param z : Geodata Z coordinate. -+ * @param nsweValue : NSWE flag to be updated. -+ * @return byte : Updated NSWE flag. -+ */ -+ private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) -+ { -+ byte nswe = nsweValue; -+ -+ // calculate virtual layer height -+ final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); -+ -+ // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) -+ final byte nsweN = getNsweBelow(x, y - 1, height); -+ final byte nsweS = getNsweBelow(x, y + 1, height); -+ final byte nsweW = getNsweBelow(x - 1, y, height); -+ final byte nsweE = getNsweBelow(x + 1, y, height); -+ -+ // north-west -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NW; -+ } -+ -+ // north-east -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NE; -+ } -+ -+ // south-west -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SW; -+ } -+ -+ // south-east -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SE; -+ } -+ -+ return nswe; -+ } -+ -+ private static byte getNsweBelow(int geoX, int geoY, short worldZ) -+ { -+ // out of geo coordinates -+ if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) -+ { -+ return 0; -+ } -+ -+ // out of geo coordinates -+ if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) -+ { -+ return 0; -+ } -+ -+ // get block -+ final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // get index, when valid, return nswe -+ final int index = block.getIndexBelow(geoX, geoY, worldZ); -+ return index == -1 ? 0 : block.getNswe(index); -+ } -+ -+ /** -+ * Save region file to file. -+ * @param filename : The name of file to save. -+ * @return boolean : True when successful. -+ */ -+ private static boolean saveGeoBlocks(String filename) -+ { -+ try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) -+ { -+ // loop over region blocks and save each block -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[ix][iy].saveBlock(bos); -+ } -+ } -+ -+ // flush data to file -+ bos.flush(); -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ private static void loadGeoengineConfigs() -+ { -+ final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); -+ Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); -+ Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); -+ Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); -+ Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); -+ Config.PATHFINDING = geoData.getBoolean("PathFinding", true); -+ Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -+ Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); -+ Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); -+ Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); -+ Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); -+ Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); -+ } -+} -\ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/geodata/Readme.txt b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/geodata/Readme.txt index a480c8c380..2611882e56 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/geodata/Readme.txt +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/geodata/Readme.txt @@ -23,8 +23,8 @@ a - Prerequisites b - Make it work ---------------------------------------------- -To make geodata working: -* unpack your geodata files into "/data/geodata" folder -* open "/config/GeoEngine.ini" with your favorite text editor and then edit following config: - - CoordSynchronize = 2 -* If you do not use any geodata files, the server will automatically change this setting to -1. +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. diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/ai/others/FleeMonsters.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/ai/others/FleeMonsters.java index 4562336103..a971742033 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/ai/others/FleeMonsters.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/ai/others/FleeMonsters.java @@ -59,7 +59,7 @@ public class FleeMonsters extends AbstractNpcAI final int posX = (int) (npc.getX() + (FLEE_DISTANCE * Math.cos(radians))); final int posY = (int) (npc.getY() + (FLEE_DISTANCE * Math.sin(radians))); final int posZ = npc.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, attacker.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); return super.onAttack(npc, attacker, damage, isSummon); } diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/ai/others/RandomWalkingGuards.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/ai/others/RandomWalkingGuards.java index b99becfdc6..780502d5d6 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/ai/others/RandomWalkingGuards.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/ai/others/RandomWalkingGuards.java @@ -55,7 +55,7 @@ public class RandomWalkingGuards extends AbstractNpcAI if (!npc.isInCombat()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, Config.MAX_DRIFT_RANGE); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("RANDOM_WALK", getRandom(MIN_WALK_DELAY, MAX_WALK_DELAY), npc, null); } diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index 524652cbc1..18511c6541 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { @@ -133,8 +133,8 @@ public class AdminGeodata implements IAdminCommandHandler } case "admin_geomap": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y + " (" + ((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + "," + ((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + " to " + ((((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + "," + ((((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + ")"); break; } diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java index f3dffa1227..76572f7f25 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java @@ -57,8 +57,8 @@ public class AdminMissingHtmls implements IAdminCommandHandler { case "admin_geomap_missing_htmls": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final int topLeftX = (x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE; final int topLeftY = (y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE; final int bottomRightX = (((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1; diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index fdb2319138..a0a34f1051 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -19,9 +19,9 @@ package handlers.admincommandhandlers; import java.util.List; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; +import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.util.BuilderUtil; @@ -44,13 +44,13 @@ public class AdminPathNode implements IAdminCommandHandler } if (activeChar.getTarget() != null) { - final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { BuilderUtil.sendSysMessage(activeChar, "No Route!"); return true; } - for (AbstractNodeLoc a : path) + for (Location a : path) { BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/Blink.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/Blink.java index 22f2c84eae..22697e8e20 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/Blink.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/Blink.java @@ -88,7 +88,7 @@ public class Blink extends AbstractEffect final int y = effected.getY() + y1; final int z = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, destination, _flyType, _flySpeed, _flyDelay, _animationSpeed)); diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/Fear.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/Fear.java index 95a5afdf98..88c795e04b 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/Fear.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/Fear.java @@ -100,7 +100,7 @@ public class Fear extends AbstractEffect final int posY = (int) (effected.getY() + (FEAR_RANGE * Math.sin(radians))); final int posZ = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); } } diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java index fddd94c60a..55c2e78d8a 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java @@ -57,7 +57,7 @@ public class FlyAway extends AbstractEffect final int y = (int) (effector.getY() - (nRadius * (dy / distance))); final int z = effector.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.broadcastPacket(new FlyToLocation(effected, destination, FlyType.THROW_UP)); effected.setXYZ(destination); diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java index 1fbd8239b0..89ad8670dc 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java @@ -133,7 +133,7 @@ public class KnockBack extends AbstractEffect final int x = (int) (effected.getX() + (_distance * Math.cos(radians))); final int y = (int) (effected.getY() + (_distance * Math.sin(radians))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, loc, _type, _speed, _delay, _animationSpeed)); diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/PullBack.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/PullBack.java index ed6a37eff6..38fe2fe9d0 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/PullBack.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/PullBack.java @@ -73,7 +73,7 @@ public class PullBack extends AbstractEffect } // In retail, you get debuff, but you are not even moved if there is obstacle. You are still disabled from using skills and moving though. - if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effector.getInstanceWorld())) + if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effected.getInstanceWorld())) { effected.broadcastPacket(new FlyToLocation(effected, effector, _type, _speed, _delay, _animationSpeed)); effected.setXYZ(effector.getX(), effector.getY(), GeoEngine.getInstance().getHeight(effector.getX(), effector.getY(), effector.getZ()) + 10); diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java index d6f9664141..813e496c47 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java @@ -87,7 +87,7 @@ public class TeleportToSummon extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = summon.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java index d3c263978c..c9dc34ac22 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java @@ -76,7 +76,7 @@ public class TeleportToTarget extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java index f2148adb54..c7ca6f64cb 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java @@ -65,7 +65,7 @@ public class RollingDice implements IItemHandler final int x = player.getX() + x1; final int y = player.getY() + y1; final int z = player.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); Broadcast.toSelfAndKnownPlayers(player, new Dice(player.getObjectId(), itemId, number, destination.getX(), destination.getY(), destination.getZ())); final SystemMessage sm = new SystemMessage(SystemMessageId.C1_HAS_ROLLED_A_S2); diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/targethandlers/Ground.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/targethandlers/Ground.java index 82ab321c29..d8c73627f5 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/targethandlers/Ground.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/dist/game/data/scripts/handlers/targethandlers/Ground.java @@ -52,7 +52,7 @@ public class Ground implements ITargetTypeHandler return null; } - if (!GeoEngine.getInstance().canSeeTarget(creature, worldPosition)) + if (!GeoEngine.getInstance().canSeeLocation(creature, worldPosition)) { if (sendMessage) { diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/Config.java index 4853969bce..c002ef6edc 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/Config.java @@ -61,6 +61,7 @@ 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; @@ -898,12 +899,17 @@ public class Config // GeoEngine // -------------------------------------------------- public static Path GEODATA_PATH; + public static GeoType GEODATA_TYPE; public static boolean PATHFINDING; public static String PATHFIND_BUFFERS; - public static float LOW_WEIGHT; - public static float MEDIUM_WEIGHT; - public static float HIGH_WEIGHT; - public static float DIAGONAL_WEIGHT; + public static int MOVE_WEIGHT; + public static int MOVE_WEIGHT_DIAG; + public static int OBSTACLE_WEIGHT; + 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; /** Attribute System */ public static int S_WEAPON_STONE; @@ -2402,12 +2408,17 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); + GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); - MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); - HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); - DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); + MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); + OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); + HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); + MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); + MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); // Load AllowedPlayerRaces config file (if exists) final PropertiesParser AllowedPlayerRaces = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE); diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/commons/util/CommonUtil.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/commons/util/CommonUtil.java index 29a88cf20c..86fa98ff10 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/commons/util/CommonUtil.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/commons/util/CommonUtil.java @@ -591,6 +591,17 @@ public class CommonUtil return formatter.format(value); } + /** + * @param numToTest : The number to test. + * @param min : The minimum limit. + * @param max : The maximum limit. + * @return the number or one of the limit (mininum / maximum). + */ + public static int limit(int numToTest, int min, int max) + { + return (numToTest > max) ? max : ((numToTest < min) ? min : numToTest); + } + public static boolean isNullOrEmpty(CharSequence value) { return isNull(value) || (value.length() == 0); diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/commons/util/Point2D.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/commons/util/Point2D.java new file mode 100644 index 0000000000..ebb6272e5e --- /dev/null +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/commons/util/Point2D.java @@ -0,0 +1,180 @@ +/* + * 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 . + */ +package org.l2jmobius.commons.util; + +import java.util.Objects; + +/** + * A datatype used to retain a 2D (x/y) point. It got the capability to be set and cleaned. + */ +public class Point2D +{ + protected volatile int _x; + protected volatile int _y; + + public Point2D(int x, int y) + { + _x = x; + _y = y; + } + + @Override + public Point2D clone() + { + return new Point2D(_x, _y); + } + + @Override + public String toString() + { + return _x + ", " + _y; + } + + @Override + public int hashCode() + { + return Objects.hash(_x, _y); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final Point2D other = (Point2D) obj; + return (_x == other._x) && (_y == other._y); + } + + /** + * @param x : The X coord to test. + * @param y : The Y coord to test. + * @return True if all coordinates equals this {@link Point2D} coordinates. + */ + public boolean equals(int x, int y) + { + return (_x == x) && (_y == y); + } + + public int getX() + { + return _x; + } + + public void setX(int x) + { + _x = x; + } + + public int getY() + { + return _y; + } + + public void setY(int y) + { + _y = y; + } + + public void set(int x, int y) + { + _x = x; + _y = y; + } + + /** + * Refresh the current {@link Point2D} using a reference {@link Point2D} and a distance. The new destination is calculated to go in opposite side of the {@link Point2D} reference.
+ *
+ * This method is perfect to calculate fleeing characters position. + * @param referenceLoc : The Point2D used as position. + * @param distance : The distance to be set between current and new position. + */ + public void setFleeing(Point2D referenceLoc, int distance) + { + final double xDiff = referenceLoc.getX() - _x; + final double yDiff = referenceLoc.getY() - _y; + + final double yxRation = Math.abs(xDiff / yDiff); + + final int y = (int) (distance / (yxRation + 1)); + final int x = (int) (y * yxRation); + + _x += (xDiff < 0 ? x : -x); + _y += (yDiff < 0 ? y : -y); + } + + public void clean() + { + _x = 0; + _y = 0; + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @return The distance between this {@Point2D} and some given coordinates. + */ + public double distance2D(int x, int y) + { + final double dx = (double) _x - x; + final double dy = (double) _y - y; + + return Math.sqrt((dx * dx) + (dy * dy)); + } + + /** + * @param point : The {@link Point2D} to test. + * @return The distance between this {@Point2D} and the {@link Point2D} set as parameter. + */ + public double distance2D(Point2D point) + { + return distance2D(point.getX(), point.getY()); + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of some given coordinates. + */ + public boolean isIn2DRadius(int x, int y, int radius) + { + return distance2D(x, y) < radius; + } + + /** + * @param point : The Point2D to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of the {@link Point2D} set as parameter. + */ + public boolean isIn2DRadius(Point2D point, int radius) + { + return distance2D(point) < radius; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/AttackableAI.java index 547963e3e8..dc29d4cf6e 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/AttackableAI.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/ai/AttackableAI.java @@ -578,7 +578,7 @@ public class AttackableAI extends CreatureAI } // Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast) - final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); + final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); moveTo(moveLoc.getX(), moveLoc.getY(), moveLoc.getZ()); } } @@ -801,7 +801,7 @@ public class AttackableAI extends CreatureAI final int newZ = npc.getZ() + 30; // Mobius: Verify destination. Prevents wall collision issues and fixes monsters not avoiding obstacles. - moveTo(GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); + moveTo(GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); } return; } diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/data/SpawnTable.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/data/SpawnTable.java index a81201f69e..19f0db5b3e 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/data/SpawnTable.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/data/SpawnTable.java @@ -114,8 +114,8 @@ public class SpawnTable } // XML file for spawn - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); // Write info to XML @@ -192,8 +192,8 @@ public class SpawnTable if (update) { - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = spawn.getNpcSpawnTemplate() != null ? spawn.getNpcSpawnTemplate().getSpawnTemplate().getFile() : new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); final File tempFile = new File(spawnFile.getAbsolutePath().substring(Config.DATAPACK_ROOT.getAbsolutePath().length() + 1).replace('\\', '/') + ".tmp"); try diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/enums/GeoType.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/enums/GeoType.java new file mode 100644 index 0000000000..f77aa5eac4 --- /dev/null +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/enums/GeoType.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.enums; + +public enum GeoType +{ + L2J("%d_%d.l2j"), + L2OFF("%d_%d_conv.dat"); + + private final String _filename; + + private GeoType(String filename) + { + _filename = filename; + } + + public String getFilename() + { + return _filename; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java new file mode 100644 index 0000000000..e62f50a8df --- /dev/null +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java @@ -0,0 +1,144 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 9d255a5f54..7bef9c770b 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,72 +16,63 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.channels.FileChannel.MapMode; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.logging.Level; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; import java.util.logging.Logger; import org.l2jmobius.Config; +import org.l2jmobius.commons.util.CommonUtil; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; -import org.l2jmobius.gameserver.geoengine.geodata.IRegion; -import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; -import org.l2jmobius.gameserver.geoengine.geodata.Region; +import org.l2jmobius.gameserver.enums.GeoType; +import org.l2jmobius.gameserver.enums.MoveDirectionType; +import org.l2jmobius.gameserver.geoengine.geodata.ABlock; +import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; +import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; +import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; +import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.model.interfaces.ILocational; -import org.l2jmobius.gameserver.util.GeoUtils; -import org.l2jmobius.gameserver.util.LinePointIterator; -import org.l2jmobius.gameserver.util.LinePointIterator3D; -/** - * @author -Nemesiss-, HorridoJoho - */ public class GeoEngine { - private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - - private static final int WORLD_MIN_X = -655360; - private static final int WORLD_MIN_Y = -589824; - private static final int WORLD_MIN_Z = -16384; - - /** 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; - - /** The regions array */ - private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; - private static final int ELEVATED_SEE_OVER_DISTANCE = 2; - private static final int MAX_SEE_OVER_HEIGHT = 48; - protected GeoEngine() + private final ABlock[][] _blocks; + private final BlockNull _nullBlock; + + // Pre-allocated buffers. + private final BufferHolder[] _buffers; + + public GeoEngine() { LOGGER.info("GeoEngine: Initializing..."); - for (int i = 0; i < _regions.length(); i++) - { - _regions.set(i, NullRegion.INSTANCE); - } + // Initialize block container. + _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; + + // Load null block. + _nullBlock = new BlockNull(); + + // Initialize multilayer temporarily buffer. + BlockMultilayer.initialize(); + + // Load geo files according to geoengine config setup. int loaded = 0; try { @@ -92,23 +83,52 @@ public class GeoEngine final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); if (Files.exists(geoFilePath)) { - try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + // Region file is load-able, try to load it. + if (loadGeoBlocks(regionX, regionY)) { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); loaded++; } } + else + { + // Region file is not load-able, load null blocks. + loadNullBlocks(regionX, regionY); + } } } } catch (Exception e) { - LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + LOGGER.warning("GeoEngine: Failed to load geodata! " + e); System.exit(1); } - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + // Release multilayer block temporarily buffer. + BlockMultilayer.release(); + + String[] array = Config.PATHFIND_BUFFERS.split(";"); + _buffers = new BufferHolder[array.length]; + + int count = 0; + for (int i = 0; i < array.length; i++) + { + String buf = array[i]; + String[] args = buf.split("x"); + + try + { + int size = Integer.parseInt(args[1]); + count += size; + _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); + } + catch (Exception e) + { + LOGGER.warning("Could not load buffer setting:" + buf + ". " + e); + } + } + LOGGER.info("Loaded " + count + " node buffers."); + // Avoid wrong configs when no files are loaded. if ((loaded == 0) && Config.PATHFINDING) { @@ -118,616 +138,913 @@ public class GeoEngine } /** - * @param geoX - * @param geoY - * @return the region + * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer and log this situation. + * @param size : pre-calculated minimal required size + * @return NodeBuffer : buffer */ - private IRegion getRegion(int geoX, int geoY) + private NodeBuffer getBuffer(int size) { - final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); - if ((region < 0) || (region >= _regions.length())) + NodeBuffer current = null; + for (BufferHolder holder : _buffers) { - return null; - } - return _regions.get(region); - } - - /** - * @param filePath - * @param regionX - * @param regionY - * @throws IOException - */ - 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()).load().order(ByteOrder.LITTLE_ENDIAN))); - } - } - - /** - * @param regionX - * @param regionY - */ - public void unloadRegion(int regionX, int regionY) - { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); - } - - /** - * @param geoX - * @param geoY - * @return if geodata exist - */ - public boolean hasGeoPos(int geoX, int geoY) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return false; - } - return region.hasGeo(); - } - - /** - * Checks the specified position for available geodata. - * @param x the world x - * @param y the world y - * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise - */ - public boolean hasGeo(int x, int y) - { - return hasGeoPos(getGeoX(x), getGeoY(y)); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe check - */ - public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return true; - } - return region.checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe anti-corner cut - */ - public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) - { - boolean can = true; - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - return can && checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the nearest Z value - */ - public int getNearestZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNearestZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next lower Z value - */ - public int getNextLowerZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextLowerZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next higher Z value - */ - public int getNextHigherZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextHigherZ(geoX, geoY, worldZ); - } - - /** - * Gets the Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHeight(int x, int y, int z) - { - return getNearestZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next lower Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getLowerHeight(int x, int y, int z) - { - return getNextLowerZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next higher Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHigherHeight(int x, int y, int z) - { - return getNextHigherZ(getGeoX(x), getGeoY(y), z); - } - - /** - * @param worldX - * @return the geo X - */ - public int getGeoX(int worldX) - { - return (worldX - WORLD_MIN_X) / 16; - } - - /** - * @param worldY - * @return the geo Y - */ - public int getGeoY(int worldY) - { - return (worldY - WORLD_MIN_Y) / 16; - } - - /** - * @param worldZ - * @return the geo Z - */ - public int getGeoZ(int worldZ) - { - return (worldZ - WORLD_MIN_Z) / 16; - } - - /** - * @param geoX - * @return the world X - */ - public int getWorldX(int geoX) - { - return (geoX * 16) + WORLD_MIN_X + 8; - } - - /** - * @param geoY - * @return the world Y - */ - public int getWorldY(int geoY) - { - return (geoY * 16) + WORLD_MIN_Y + 8; - } - - /** - * @param geoZ - * @return the world Z - */ - public int getWorldZ(int geoZ) - { - return (geoZ * 16) + WORLD_MIN_Z + 8; - } - - /** - * Can see target. Doors as target always return true. Checks doors between. - * @param cha the character - * @param target the target - * @return {@code true} if the character can see the target (LOS), {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, WorldObject target) - { - if (target.isDoor()) - { - // Can always see doors. - return true; - } - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); - } - - /** - * Can see target. Checks doors between. - * @param cha the character - * @param worldPosition the world position - * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) - { - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param world - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @param tworld the target's instanceId - * @return - */ - public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) - { - return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instance - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) - { - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) - { - return false; - } - return canSeeTarget(x, y, z, tx, ty, tz); - } - - /** - * @param prevX - * @param prevY - * @param prevGeoZ - * @param curX - * @param curY - * @param nswe - * @return the LOS Z value - */ - private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) - { - if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) - { - throw new RuntimeException("Multiple directions!"); - } - - if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) - { - return getNearestZ(curX, curY, prevGeoZ); - } - - return getNextHigherZ(curX, curY, prevGeoZ); - } - - /** - * Can see target. Does not check doors between. - * @param xValue the x coordinate - * @param yValue the y coordinate - * @param zValue the z coordinate - * @param txValue the target's x coordinate - * @param tyValue the target's y coordinate - * @param tzValue the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) - { - int x = xValue; - int y = yValue; - int tx = txValue; - int ty = tyValue; - - int geoX = getGeoX(x); - int geoY = getGeoY(y); - int tGeoX = getGeoX(tx); - int tGeoY = getGeoY(ty); - - int z = getNearestZ(geoX, geoY, zValue); - int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - // Fastpath. - if ((geoX == tGeoX) && (geoY == tGeoY)) - { - if (hasGeoPos(tGeoX, tGeoY)) - { - return z == tz; - } - return true; - } - - if (tz > z) - { - int tmp = tx; - tx = x; - x = tmp; - - tmp = ty; - ty = y; - y = tmp; - - tmp = tz; - tz = z; - z = tmp; - - tmp = tGeoX; - tGeoX = geoX; - geoX = tmp; - - tmp = tGeoY; - tGeoY = geoY; - geoY = tmp; - } - - final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); - // First point is guaranteed to be available, skip it, we can always see our own position. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - final int prevZ = pointIter.z(); - int prevGeoZ = prevZ; - int ptIndex = 0; - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - - if ((curX == prevX) && (curY == prevY)) + // Find proper size of buffer. + if (holder._size < size) { continue; } - final int beeCurZ = pointIter.z(); - int curGeoZ = prevGeoZ; - - // Check if the position has geodata. - if (hasGeoPos(curX, curY)) + // Find unlocked NodeBuffer. + for (NodeBuffer buffer : holder._buffer) { - final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); - curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); - int maxHeight; - if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) + if (!buffer.isLocked()) { - maxHeight = z + MAX_SEE_OVER_HEIGHT; - } - else - { - maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; + continue; } - boolean canSeeThrough = false; - if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) + return buffer; + } + + LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); + + // NodeBuffer not found, allocate temporary buffer. + current = new NodeBuffer(holder._size); + current.isLocked(); + } + + return current; + } + + /** + * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + * @return boolean : True, when geodata file was loaded without problem. + */ + private boolean loadGeoBlocks(int regionX, int regionY) + { + final String filename = String.format(Config.GEODATA_TYPE.getFilename(), regionX, regionY); + final String filepath = Config.GEODATA_PATH + "\\" + filename; + + // Standard load. + try (RandomAccessFile raf = new RandomAccessFile(filepath, "r"); + FileChannel fc = raf.getChannel()) + { + // Initialize file buffer. + MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // Load 18B header for L2OFF geodata (1st and 2nd byte...region X and Y). + if (Config.GEODATA_TYPE == GeoType.L2OFF) + { + for (int i = 0; i < 18; i++) { - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) + buffer.get(); + } + } + + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Loop over region blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + if (Config.GEODATA_TYPE == GeoType.L2J) { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) - { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); + // Get block type. + final byte type = buffer.get(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + case GeoStructure.TYPE_MULTILAYER_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + default: + { + throw new IllegalArgumentException("Unknown block type: " + type); + } + } } else { - canSeeThrough = true; + // Get block type. + final short type = buffer.getShort(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + default: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + } } } + } + + // Check data consistency. + if (buffer.remaining() > 0) + { + LOGGER.warning("Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); + } + + // Loading was successful. + return true; + } + catch (Exception e) + { + // An error occured while loading, load null blocks. + LOGGER.warning("Error loading " + filename + " region file. " + e); + + // Replace whole region file with null blocks. + loadNullBlocks(regionX, regionY); + + // Loading was not successful. + return false; + } + } + + /** + * Loads null blocks. Used when no region file is detected or an error occurs during loading. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + */ + private void loadNullBlocks(int regionX, int regionY) + { + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Load all null blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + _blocks[blockX + ix][blockY + iy] = _nullBlock; + } + } + } + + /** + * Converts world X to geodata X. + * @param worldX + * @return int : Geo X + */ + public static int getGeoX(int worldX) + { + return (worldX - World.WORLD_X_MIN) >> 4; + } + + /** + * Converts world Y to geodata Y. + * @param worldY + * @return int : Geo Y + */ + public static int getGeoY(int worldY) + { + return (worldY - World.WORLD_Y_MIN) >> 4; + } + + /** + * Converts geodata X to world X. + * @param geoX + * @return int : World X + */ + public static int getWorldX(int geoX) + { + return (geoX << 4) + World.WORLD_X_MIN + 8; + } + + /** + * Converts geodata Y to world Y. + * @param geoY + * @return int : World Y + */ + public static int getWorldY(int geoY) + { + return (geoY << 4) + World.WORLD_Y_MIN + 8; + } + + /** + * Returns block of geodata on given coordinates. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return {@link ABlock} : Block of geodata. + */ + public ABlock getBlock(int geoX, int geoY) + { + final int x = geoX / GeoStructure.BLOCK_CELLS_X; + if ((x < 0) || (x >= GeoStructure.GEO_BLOCKS_X)) + { + return null; + } + final int y = geoY / GeoStructure.BLOCK_CELLS_Y; + if ((y < 0) || (y >= GeoStructure.GEO_BLOCKS_Y)) + { + return null; + } + return _blocks[x][y]; + } + + /** + * Check if geo coordinates has geo. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return boolean : True, if given geo coordinates have geodata + */ + public boolean hasGeoPos(int geoX, int geoY) + { + final ABlock block = getBlock(geoX, geoY); + return (block != null) && block.hasGeoPos(); + } + + /** + * Returns the height of cell, which is closest to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell geodata Z coordinate, closest to given coordinates. + */ + public short getHeightNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return (short) worldZ; + } + return block.getHeightNearest(geoX, geoY, worldZ); + } + + /** + * Returns the NSWE flag byte of cell, which is closes to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. + */ + public byte getNsweNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return GeoStructure.CELL_FLAG_ALL; + } + return block.getNsweNearest(geoX, geoY, worldZ); + } + + /** + * Check if world coordinates has geo. + * @param worldX : World X + * @param worldY : World Y + * @return boolean : True, if given world coordinates have geodata + */ + public boolean hasGeo(int worldX, int worldY) + { + return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param loc : The location used as reference. + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(Location loc) + { + return getHeightNearest(getGeoX(loc.getX()), getGeoY(loc.getY()), loc.getZ()); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param worldX : world x + * @param worldY : world y + * @param worldZ : world z + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(int worldX, int worldY, int worldZ) + { + return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); + } + + /** + * Check line of sight from {@link WorldObject} to {@link WorldObject}.
+ * @param object : The origin object. + * @param target : The target object. + * @return True, when object can see target. + */ + public boolean canSeeTarget(WorldObject object, WorldObject target) + { + // Can always see doors. + if (target.isDoor()) + { + return true; + } + + if (object.getInstanceWorld() != target.getInstanceWorld()) + { + return false; + } + + if (DoorData.getInstance().checkIfDoorsBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld())) + { + return false; + } + + // Get object's and target's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + double theight = 0; + if (target instanceof Creature) + { + theight += (((Creature) target).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + return canSee(object.getX(), object.getY(), object.getZ(), oheight, target.getX(), target.getY(), target.getZ(), theight) && canSee(target.getX(), target.getY(), target.getZ(), theight, object.getX(), object.getY(), object.getZ(), oheight); + } + + /** + * Check line of sight from {@link WorldObject} to {@link Location}.
+ * Note: The check uses {@link Location}'s real Z coordinate (e.g. point above ground), not its geodata representation. + * @param object : The origin object. + * @param position : The target position. + * @return True, when object can see position. + */ + public boolean canSeeLocation(WorldObject object, Location position) + { + // Get object and location coordinates. + int ox = object.getX(); + int oy = object.getY(); + int oz = object.getZ(); + int tx = position.getX(); + int ty = position.getY(); + int tz = position.getZ(); + + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld())) + { + return false; + } + + // Get object's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + // Perform geodata check. + return canSee(ox, oy, oz, oheight, tx, ty, tz, 0) && canSee(tx, ty, tz, 0, ox, oy, oz, oheight); + } + + /** + * Simple check for origin to target visibility.
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param oheight : The height of origin, used as start point. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param theight : The height of target, used as end point. + * @return True, when origin can see target. + */ + public boolean canSee(int ox, int oy, int oz, double oheight, int tx, int ty, int tz, double theight) + { + // Get origin geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get target geodata coordinates. + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check being on same cell and layer (index). + // Note: Get index must use origin height increased by cell height, the method returns index to height exclusive self. + int index = block.getIndexBelow(gox, goy, oz + GeoStructure.CELL_HEIGHT); + if (index < 0) + { + return false; + } + + if ((gox == gtx) && (goy == gty)) + { + return index == block.getIndexBelow(gtx, gty, tz + GeoStructure.CELL_HEIGHT); + } + + // Get ground and nswe flag. + int groundZ = block.getHeight(index); + int nswe = block.getNswe(index); + + // Get delta coordinates, slope of line (XY, XZ) and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double dz = (tz + theight) - (oz + oheight); + final double m = (double) dy / dx; + final double mz = dz / Math.sqrt((dx * dx) + (dy * dy)); + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); + + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) + { + // Set next cell in X direction. + gridX += mdt.getStepX(); + gox += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); - if (!canSeeThrough) - { - return false; - } + // Set next cell in Y direction. + gridY += mdt.getStepY(); + goy += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevGeoZ = curGeoZ; - ++ptIndex; + // Get block of the next cell. + block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get line of sight height (including Z slope). + double losz = oz + oheight + Config.MAX_OBSTACLE_HEIGHT; + losz += mz * Math.sqrt(((checkX - ox) * (checkX - ox)) + ((checkY - oy) * (checkY - oy))); + + // Check line of sight going though wall (vertical check). + + // Get index of particular layer, based on last iterated cell conditions. + boolean canMove = (nswe & dir) != 0; + if (canMove) + { + // No wall present, get next cell below current cell. + index = block.getIndexBelow(gox, goy, groundZ + GeoStructure.CELL_IGNORE_HEIGHT); + } + else + { + // Wall present, get next cell above current cell. + index = block.getIndexAbove(gox, goy, groundZ - (2 * GeoStructure.CELL_HEIGHT)); + } + + // Next cell's does not exist (no geodata with valid condition), return fail. + if (index < 0) + { + return false; + } + + // Get next cell's layer height. + int z = block.getHeight(index); + + // Perform sine of sight check (next cell is above line of sight line), return fail. + if (z > losz) + { + return false; + } + + // Next cell is accessible, update z and NSWE. + groundZ = z; + nswe = block.getNswe(index); } + // Iteration is completed, no obstacle is found. return true; } /** - * Move check. - * @param x the x coordinate - * @param y the y coordinate - * @param zValue the z coordinate - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tzValue the target's z coordinate - * @param instance the instance - * @return the last Location (x,y,z) where player can walk - just before wall + * Check movement from coordinates to coordinates.
+ * Note: The Z coordinates are supposed to be already validated geodata coordinates. + * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return True, when target coordinates are reachable from origin coordinates. */ - public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(x); - final int geoY = getGeoY(y); - final int z = getNearestZ(geoX, geoY, zValue); - final int tGeoX = getGeoX(tx); - final int tGeoY = getGeoY(ty); - final int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return new Location(x, y, getHeight(x, y, z)); - } - if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) - { - return new Location(x, y, getHeight(x, y, z)); + return false; } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = z; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return false; + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + int goz = getHeightNearest(gox, goy, oz); + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check movement within same cell. + if ((gox == gtx) && (goy == gty)) + { + return goz == getHeight(tx, ty, tz); + } + + // Get nswe flag. + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - // Can't move, return previous location. - return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check point heading into obstacle, if so return current point. + if ((nswe & dir) == 0) + { + return false; + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return false; + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != tz)) - { - // Different floors, return start location. - return new Location(x, y, z); - } - - return new Location(tx, ty, tz); + // When origin Z is target Z, the move is successful. + return goz == getHeight(tx, ty, tz); } /** - * Checks if its possible to move from one location to another. - * @param fromX the X coordinate to start checking from - * @param fromY the Y coordinate to start checking from - * @param fromZvalue the Z coordinate to start checking from - * @param toX the X coordinate to end checking at - * @param toY the Y coordinate to end checking at - * @param toZvalue the Z coordinate to end checking at - * @param instance the instance - * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + * Check movement from origin to target coordinates. Returns last available point in the checked path.
+ * Target X and Y reachable and Z is on same floor: + *
    + *
  • Location of the target with corrected Z value from geodata.
  • + *
+ * Target X and Y reachable but Z is on another floor: + *
    + *
  • Location of the origin with corrected Z value from geodata.
  • + *
+ * Target X and Y not reachable: + *
    + *
  • Last accessible location in destination to target.
  • + *
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return The {@link Location} representing last point of movement (e.g. just before wall). */ - public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + public Location getValidLocation(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(fromX); - final int geoY = getGeoY(fromY); - final int fromZ = getNearestZ(geoX, geoY, fromZvalue); - final int tGeoX = getGeoX(toX); - final int tGeoY = getGeoY(toY); - final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); - - if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) - { - return false; + return new Location(ox, oy, oz); } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = fromZ; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return new Location(ox, oy, oz); + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + final int gtz = getHeightNearest(gtx, gty, tz); + int goz = getHeightNearest(gox, goy, oz); + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - return false; - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check target cell is out of geodata grid (world coordinates). + if ((nx < 0) || (nx >= GeoStructure.GEO_CELLS_X) || (ny < 0) || (ny >= GeoStructure.GEO_CELLS_Y)) + { + return new Location(checkX, checkY, goz); + } + + // Check point heading into obstacle, if so return current (border) point. + if ((nswe & dir) == 0) + { + return new Location(checkX, checkY, goz); + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current (border) point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return new Location(checkX, checkY, goz); + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) - { - // Different floors. - return false; - } - - return true; + // Compare Z coordinates: + // If same, path is okay, return target point and fix its Z geodata coordinate. + // If not same, path is does not exist, return origin point. + return goz == gtz ? new Location(tx, ty, gtz) : new Location(ox, oy, oz); } + /** + * Returns the list of location objects as a result of complete path calculation. + * @param ox : origin x + * @param oy : origin y + * @param oz : origin z + * @param tx : target x + * @param ty : target y + * @param tz : target z + * @param instance + * @return {@code List} : complete path from nodes + */ + public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + { + // Get origin and check existing geo coords. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + if (!hasGeoPos(gox, goy)) + { + return Collections.emptyList(); + } + + int goz = getHeightNearest(gox, goy, oz); + + // Get target and check existing geo coords. + int gtx = getGeoX(tx); + int gty = getGeoY(ty); + if (!hasGeoPos(gtx, gty)) + { + return Collections.emptyList(); + } + + int gtz = getHeightNearest(gtx, gty, tz); + + // Prepare buffer for pathfinding calculations. + NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + if (buffer == null) + { + return Collections.emptyList(); + } + + // Find path. + List path = null; + try + { + path = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + if (path.isEmpty()) + { + return Collections.emptyList(); + } + } + catch (Exception e) + { + return Collections.emptyList(); + } + finally + { + buffer.free(); + } + + // Check path. + if (path.size() < 3) + { + return path; + } + + // Get path list iterator. + ListIterator point = path.listIterator(); + + // Get node A (origin). + int nodeAx = ox; + int nodeAy = oy; + int nodeAz = goz; + + // Get node B. + Location nodeB = point.next(); + + // Iterate thought the path to optimize it. + while (point.hasNext()) + { + // Get node C. + Location nodeC = path.get(point.nextIndex()); + + // Check movement from node A to node C. + if (canMoveToTarget(nodeAx, nodeAy, nodeAz, nodeC.getX(), nodeC.getY(), nodeC.getZ(), instance)) + { + // Can move from node A to node C. + // Remove node B. + point.remove(); + } + else + { + // Can not move from node A to node C. + // Set node A (node B is part of path, update A coordinates). + nodeAx = nodeB.getX(); + nodeAy = nodeB.getY(); + nodeAz = nodeB.getZ(); + } + + // Set node B. + nodeB = point.next(); + } + + return path; + } + + /** + * NodeBuffer container with specified size and count of separate buffers. + */ + private static class BufferHolder + { + final int _size; + final List _buffer; + + public BufferHolder(int size, int count) + { + _size = size; + _buffer = new ArrayList<>(count); + + for (int i = 0; i < count; i++) + { + _buffer.add(new NodeBuffer(size)); + } + } + } + + /** + * Returns the instance of the {@link GeoEngine}. + * @return {@link GeoEngine} : The instance. + */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -737,4 +1054,4 @@ public class GeoEngine { protected static final GeoEngine INSTANCE = new GeoEngine(); } -} +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java deleted file mode 100644 index cc76b7523a..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ /dev/null @@ -1,301 +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 . - */ -package org.l2jmobius.gameserver.geoengine; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; -import org.l2jmobius.gameserver.model.World; -import org.l2jmobius.gameserver.model.instancezone.Instance; - -/** - * @author -Nemesiss- - */ -public class GeoEnginePathfinding -{ - private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); - - private BufferInfo[] _buffers; - - protected GeoEnginePathfinding() - { - try - { - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - - _buffers = 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); - } - - _buffers[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"); - } - } - - public boolean pathNodesExist(short regionoffset) - { - return false; - } - - public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) - { - 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)))); - if (buffer == null) - { - return null; - } - - List path = null; - try - { - final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); - - if (result == null) - { - return null; - } - - path = constructPath(result); - } - catch (Exception e) - { - LOGGER.warning(e.getMessage()); - return null; - } - finally - { - buffer.free(); - } - - // check path - if (path.size() < 3) - { - return path; - } - - int currentX, currentY, currentZ; - ListIterator middlePoint; - - middlePoint = path.listIterator(); - currentX = x; - currentY = y; - currentZ = z; - - while (middlePoint.hasNext()) - { - final AbstractNodeLoc locMiddle = middlePoint.next(); - if (!middlePoint.hasNext()) - { - break; - } - - final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); - if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) - { - middlePoint.remove(); - } - else - { - currentX = locMiddle.getX(); - currentY = locMiddle.getY(); - currentZ = locMiddle.getZ(); - } - } - - return path; - } - - /** - * Convert geodata position to pathnode position - * @param geo_pos - * @return pathnode position - */ - public short getNodePos(int geo_pos) - { - return (short) (geo_pos >> 3); // OK? - } - - /** - * Convert node position to pathnode block position - * @param node_pos - * @return pathnode block position (0...255) - */ - public short getNodeBlock(int node_pos) - { - return (short) (node_pos % 256); - } - - public byte getRegionX(int node_pos) - { - return (byte) ((node_pos >> 8) + World.TILE_X_MIN); - } - - public byte getRegionY(int node_pos) - { - return (byte) ((node_pos >> 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 node_x rx - * @return - */ - public int calculateWorldX(short node_x) - { - return World.MAP_MIN_X + (node_x * 128) + 48; - } - - /** - * Convert pathnode y to World y position - * @param node_y - * @return - */ - public int calculateWorldY(short node_y) - { - return World.MAP_MIN_Y + (node_y * 128) + 48; - } - - private List constructPath(AbstractNode nodeValue) - { - final LinkedList path = new LinkedList<>(); - int previousDirectionX = Integer.MIN_VALUE; - int previousDirectionY = Integer.MIN_VALUE; - int directionX, directionY; - - AbstractNode node = nodeValue; - while (node.getParent() != null) - { - directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); - directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); - - // only add a new route point if moving direction changes - if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) - { - previousDirectionX = directionX; - previousDirectionY = directionY; - - path.addFirst(node.getLoc()); - node.setLoc(null); - } - - node = node.getParent(); - } - - return path; - } - - private CellNodeBuffer alloc(int size) - { - CellNodeBuffer current = null; - for (BufferInfo i : _buffers) - { - if (i.mapSize >= size) - { - for (CellNodeBuffer buf : i.buffer) - { - if (buf.lock()) - { - current = buf; - break; - } - } - if (current != null) - { - break; - } - - // not found, allocate temporary buffer - current = new CellNodeBuffer(i.mapSize); - current.lock(); - if (i.buffer.size() < i.count) - { - i.buffer.add(current); - break; - } - } - } - - return current; - } - - private static final class BufferInfo - { - final int mapSize; - final int count; - ArrayList buffer; - - public BufferInfo(int size, int cnt) - { - mapSize = size; - count = cnt; - buffer = new ArrayList<>(count); - } - } - - public static GeoEnginePathfinding getInstance() - { - return SingletonHolder.INSTANCE; - } - - private static class SingletonHolder - { - protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); - } -} diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java new file mode 100644 index 0000000000..49ec35aa1c --- /dev/null +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java @@ -0,0 +1,85 @@ +/* + * 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 . + */ +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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @param index : Index of the cell. + * @return short : Cell geodata Z coordinate, below given coordinates. + */ + public abstract byte getNswe(int index); +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java new file mode 100644 index 0000000000..f5997bf287 --- /dev/null +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java @@ -0,0 +1,130 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java new file mode 100644 index 0000000000..9af9d2a28a --- /dev/null +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java @@ -0,0 +1,95 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java new file mode 100644 index 0000000000..31fbf5d477 --- /dev/null +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java new file mode 100644 index 0000000000..f77d21d84b --- /dev/null +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java @@ -0,0 +1,68 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +public class BlockNull extends ABlock +{ + @Override + public boolean hasGeoPos() + { + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java deleted file mode 100644 index 61ea4fcf82..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java +++ /dev/null @@ -1,48 +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 . - */ -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() - { - } -} diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java deleted file mode 100644 index 3c8de1a7f7..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java +++ /dev/null @@ -1,77 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java deleted file mode 100644 index 1ccdefe3da..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java +++ /dev/null @@ -1,56 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java new file mode 100644 index 0000000000..691b164e0c --- /dev/null +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ +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_ALL = 0x0F; + + // 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; +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java deleted file mode 100644 index 25eafc5a04..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java +++ /dev/null @@ -1,42 +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 . - */ -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); -} diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java deleted file mode 100644 index 0d2c765002..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java +++ /dev/null @@ -1,47 +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 . - */ -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(); -} diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java deleted file mode 100644 index 52df457360..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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); - - // 1 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; - } -} diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java deleted file mode 100644 index 72a5a635c7..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java +++ /dev/null @@ -1,55 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author HorridoJoho - */ -public final class NullRegion implements IRegion -{ - public static final NullRegion INSTANCE = new NullRegion(); - - @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 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; - } -} diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java deleted file mode 100644 index fc924cb875..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java +++ /dev/null @@ -1,92 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java deleted file mode 100644 index 5087513ed9..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java +++ /dev/null @@ -1,87 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public abstract class AbstractNode -{ - private T _loc; - private AbstractNode _parent; - - public AbstractNode(T loc) - { - _loc = loc; - } - - public void setParent(AbstractNode p) - { - _parent = p; - } - - public AbstractNode getParent() - { - return _parent; - } - - public T getLoc() - { - return _loc; - } - - public void setLoc(T l) - { - _loc = l; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); - return result; - } - - @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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java deleted file mode 100644 index 7223b5b2be..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java +++ /dev/null @@ -1,33 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -/** - * @author -Nemesiss- - */ -public abstract class AbstractNodeLoc -{ - public abstract int getX(); - - public abstract int getY(); - - public abstract int getZ(); - - public abstract int getNodeX(); - - public abstract int getNodeY(); -} diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java deleted file mode 100644 index 3425234e0e..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public class CellNode extends AbstractNode -{ - 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java deleted file mode 100644 index 522610bb7c..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java +++ /dev/null @@ -1,348 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -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 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) - { - _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(); - } - - public final List debugPath() - { - final List 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 void getNeighbors() - { - if (!_current.getLoc().canGoAll()) - { - 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); - } - - // SouthEast - if ((nodeE != null) && (nodeS != null)) - { - if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) - { - addNode(x + 1, y + 1, z, true); - } - } - - // SouthWest - if ((nodeS != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) - { - addNode(x - 1, y + 1, z, true); - } - } - - // NorthEast - if ((nodeN != null) && (nodeE != null)) - { - if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) - { - addNode(x + 1, y - 1, z, true); - } - } - - // NorthWest - if ((nodeN != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) - { - addNode(x - 1, y - 1, z, true); - } - } - } - - private 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(); - // reinit node if needed - if (result.getLoc() != null) - { - result.getLoc().set(x, y, z); - } - else - { - result.setLoc(new NodeLoc(x, y, z)); - } - } - - return result; - } - - private 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 boolean isHighWeight(int x, int y, int z) - { - final CellNode result = getNode(x, y, z); - if (result == null) - { - return true; - } - - if (!result.getLoc().canGoAll()) - { - return true; - } - if (Math.abs(result.getLoc().getZ() - z) > 16) - { - return true; - } - - return false; - } - - private 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java new file mode 100644 index 0000000000..fa5ca43c2f --- /dev/null +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -0,0 +1,111 @@ +/* + * 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 . + */ +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 geodata values. + private int _geoX; + private int _geoY; + private byte _nswe; + + // 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; + + _costG = 0; + _costH = 0; + _costF = 0; + + _parent = null; + } + + public void setGeo(int gx, int gy, int gz, byte nswe) + { + super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); + + _geoX = gx; + _geoY = gy; + _nswe = nswe; + } + + 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 int getCostF() + { + return _costF; + } + + public Node getParent() + { + return _parent; + } + + @Override + public int compareTo(Node o) + { + return _costF - o._costF; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java new file mode 100644 index 0000000000..b464212c96 --- /dev/null +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -0,0 +1,368 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.awt.Color; +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; +import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; + +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 _opened; + // Container holding Nodes already explored. + private final List _closed; + + // Target coordinates. + private int _gtx; + private int _gty; + private int _gtz; + + // Pathfinding statistics. + private long _timeStamp; + private long _lastElapsedTime; + + 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 findPath(int gox, int goy, int goz, int gtx, int gty, int gtz) + { + // Set start timestamp. + _timeStamp = System.currentTimeMillis(); + + // 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)); + _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 constructPath() + { + // Create result. + final LinkedList 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; + } + + /** + * Creates list of Nodes to show debug path. + * @param debug : The debug packet to add debug informations in. + */ + public void debugPath(ExServerPrimitive debug) + { + // Add all opened node as yellow points. + for (Node n : _opened) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); + } + + // Add all opened node as blue points. + for (Node n : _closed) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); + } + } + + 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; + + _lastElapsedTime = System.currentTimeMillis() - _timeStamp; + _lock.unlock(); + } + + public long getElapsedTime() + { + return _lastElapsedTime; + } + + /** + * Expand the current {@link Node} by exploring its neighbors (axially and diagonally). + */ + private void expand() + { + // Movement is blocked, skip. + final byte nswe = _current.getNSWE(); + if (nswe == 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; + + // Can move north, expand. + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + } + + // Can move south, expand. + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + } + + // Can move west, expand. + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move east, expand. + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move north-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move north-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + } + + /** + * 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 weight : The weight of movement to the new node. + * @return The nswe of the added node. Blank, if not added. + */ + private byte addNode(int gx, int gy, int gzValue, int weight) + { + // 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]; + + // Set node geodata coordinates. + node.setGeo(gx, gy, gz, nswe); + + // 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, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + _opened.add(node); + _bufferIndex++; + return nswe; + } + + /** + * Calculate cost H value, calculated using diagonal distance method.
+ * 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. + final int dx = Math.abs(gx - _gtx); + final int dy = Math.abs(gy - _gty); + final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + + // Get diagonal and axial differences to the target. + final int dd = Math.min(dx, dy); + final int da = Math.max(dx, dy) - dd; + + // Calculate the diagonal distance of the node to the target. + return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java deleted file mode 100644 index e3bad04b25..0000000000 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; - -/** - * @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 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 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; - // return super.hashCode(); - } - - @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; - if (_x != other._x) - { - return false; - } - if (_y != other._y) - { - return false; - } - if (_goNorth != other._goNorth) - { - return false; - } - if (_goEast != other._goEast) - { - return false; - } - if (_goSouth != other._goSouth) - { - return false; - } - if (_goWest != other._goWest) - { - return false; - } - if (_geoHeight != other._geoHeight) - { - return false; - } - return true; - } -} diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java index 1faf1ae423..ed261765e1 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java @@ -93,15 +93,15 @@ public class ZoneManager implements IXmlReader private static final Map SETTINGS = new HashMap<>(); private static final int SHIFT_BY = 15; - private static final int OFFSET_X = Math.abs(World.MAP_MIN_X >> SHIFT_BY); - private static final int OFFSET_Y = Math.abs(World.MAP_MIN_Y >> SHIFT_BY); + private static final int OFFSET_X = Math.abs(World.WORLD_X_MIN >> SHIFT_BY); + private static final int OFFSET_Y = Math.abs(World.WORLD_Y_MIN >> SHIFT_BY); private final Map, ConcurrentHashMap> _classZones = new ConcurrentHashMap<>(); private final Map _spawnTerritories = new ConcurrentHashMap<>(); private final AtomicInteger _lastDynamicId = new AtomicInteger(300000); private List _debugItems; - private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.MAP_MAX_X >> SHIFT_BY) + OFFSET_X + 1][(World.MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y + 1]; + private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.WORLD_X_MAX >> SHIFT_BY) + OFFSET_X + 1][(World.WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y + 1]; /** * Instantiates a new zone manager. diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/Location.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/Location.java index 73689ab1a6..ca3b22a3f0 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/Location.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/Location.java @@ -16,30 +16,30 @@ */ package org.l2jmobius.gameserver.model; +import java.util.Objects; + +import org.l2jmobius.commons.util.Point2D; import org.l2jmobius.gameserver.model.interfaces.ILocational; import org.l2jmobius.gameserver.model.interfaces.IPositionable; /** - * Location data transfer object.
- * Contains coordinates data, heading and instance Id. - * @author Zoey76 + * A datatype used to retain a 3D (x/y/z/heading) point. It got the capability to be set and cleaned. */ -public class Location implements IPositionable +public class Location extends Point2D implements IPositionable { - protected int _x; - protected int _y; - protected int _z; - private int _heading; + protected volatile int _z; + protected volatile int _heading; public Location(int x, int y, int z) { - this(x, y, z, 0); + super(x, y); + _z = z; + _heading = 0; } public Location(int x, int y, int z, int heading) { - _x = x; - _y = y; + super(x, y); _z = z; _heading = heading; } @@ -51,9 +51,8 @@ public class Location implements IPositionable public Location(StatSet set) { - _x = set.getInt("x"); - _y = set.getInt("y"); - _z = set.getInt("z"); + super(set.getInt("x", 0), set.getInt("y", 0)); + _z = set.getInt("z", 0); _heading = set.getInt("heading", 0); } @@ -146,6 +145,25 @@ public class Location implements IPositionable _heading = loc.getHeading(); } + @Override + public void clean() + { + super.clean(); + _z = 0; + } + + @Override + public Location clone() + { + return new Location(_x, _y, _z); + } + + @Override + public int hashCode() + { + return (31 * super.hashCode()) + Objects.hash(_z); + } + @Override public boolean equals(Object obj) { diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/World.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/World.java index c1510e8c0e..0d796dd834 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/World.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/World.java @@ -66,19 +66,19 @@ public class World public static final int TILE_Y_MAX = 26; public static final int TILE_ZERO_COORD_X = 20; public static final int TILE_ZERO_COORD_Y = 18; - public static final int MAP_MIN_X = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; - public static final int MAP_MIN_Y = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; + public static final int WORLD_X_MIN = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; + public static final int WORLD_Y_MIN = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; - public static final int MAP_MAX_X = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; - public static final int MAP_MAX_Y = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; + public static final int WORLD_X_MAX = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; + public static final int WORLD_Y_MAX = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; /** Calculated offset used so top left region is 0,0 */ - public static final int OFFSET_X = Math.abs(MAP_MIN_X >> SHIFT_BY); - public static final int OFFSET_Y = Math.abs(MAP_MIN_Y >> SHIFT_BY); + public static final int OFFSET_X = Math.abs(WORLD_X_MIN >> SHIFT_BY); + public static final int OFFSET_Y = Math.abs(WORLD_Y_MIN >> SHIFT_BY); /** Number of regions. */ - private static final int REGIONS_X = (MAP_MAX_X >> SHIFT_BY) + OFFSET_X; - private static final int REGIONS_Y = (MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y; + private static final int REGIONS_X = (WORLD_X_MAX >> SHIFT_BY) + OFFSET_X; + private static final int REGIONS_Y = (WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y; /** Map containing all the players in game. */ private static final Map _allPlayers = new ConcurrentHashMap<>(); @@ -817,9 +817,6 @@ public class World return _memberInPartyNumber.get(); } - /** - * @return the current instance of World - */ public static World getInstance() { return SingletonHolder.INSTANCE; diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/WorldObject.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/WorldObject.java index 0e5536bbc0..62313ffea1 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/WorldObject.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/WorldObject.java @@ -188,23 +188,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif synchronized (this) { int spawnX = x; - if (spawnX > World.MAP_MAX_X) + if (spawnX > World.WORLD_X_MAX) { - spawnX = World.MAP_MAX_X - 5000; + spawnX = World.WORLD_X_MAX - 5000; } - if (spawnX < World.MAP_MIN_X) + if (spawnX < World.WORLD_X_MIN) { - spawnX = World.MAP_MIN_X + 5000; + spawnX = World.WORLD_X_MIN + 5000; } int spawnY = y; - if (spawnY > World.MAP_MAX_Y) + if (spawnY > World.WORLD_Y_MAX) { - spawnY = World.MAP_MAX_Y - 5000; + spawnY = World.WORLD_Y_MAX - 5000; } - if (spawnY < World.MAP_MIN_Y) + if (spawnY < World.WORLD_Y_MIN) { - spawnY = World.MAP_MIN_Y + 5000; + spawnY = World.WORLD_Y_MIN + 5000; } // Set the x,y,z position of the WorldObject. If flagged with _isSpawned, setXYZ will automatically update world region, so avoid that. @@ -518,23 +518,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif public void setXYZInvisible(int x, int y, int z) { int correctX = x; - if (correctX > World.MAP_MAX_X) + if (correctX > World.WORLD_X_MAX) { - correctX = World.MAP_MAX_X - 5000; + correctX = World.WORLD_X_MAX - 5000; } - if (correctX < World.MAP_MIN_X) + if (correctX < World.WORLD_X_MIN) { - correctX = World.MAP_MIN_X + 5000; + correctX = World.WORLD_X_MIN + 5000; } int correctY = y; - if (correctY > World.MAP_MAX_Y) + if (correctY > World.WORLD_Y_MAX) { - correctY = World.MAP_MAX_Y - 5000; + correctY = World.WORLD_Y_MAX - 5000; } - if (correctY < World.MAP_MIN_Y) + if (correctY < World.WORLD_Y_MIN) { - correctY = World.MAP_MIN_Y + 5000; + correctY = World.WORLD_Y_MIN + 5000; } setXYZ(correctX, correctY, z); diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/Creature.java index 32f014e233..acf6ab07ff 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -66,8 +66,6 @@ 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.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -2586,7 +2584,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3419,8 +3417,8 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe final int originalX = x; final int originalY = y; final int originalZ = z; - final int gtx = (originalX - World.MAP_MIN_X) >> 4; - final int gty = (originalY - World.MAP_MIN_Y) >> 4; + final int gtx = (originalX - World.WORLD_X_MIN) >> 4; + final int gty = (originalY - World.WORLD_Y_MIN) >> 4; if (isOnGeodataPath()) { try @@ -3443,7 +3441,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe && !(((curZ - z) > 300) && (distance < 300))) // Prohibit correcting destination if character wants to fall. { // location different if destination wasn't reached (or just z coord is different) - final Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(curX, curY, curZ, x, y, z, getInstanceWorld()); + final Location destiny = GeoEngine.getInstance().getValidLocation(curX, curY, curZ, x, y, z, getInstanceWorld()); x = destiny.getX(); y = destiny.getY(); z = destiny.getZ(); @@ -3457,7 +3455,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { if (isPlayer() && !_isFlying && !isInWater) diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/Summon.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/Summon.java index 03d1af23ea..be89eb6407 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/Summon.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/actor/Summon.java @@ -106,7 +106,7 @@ public abstract class Summon extends Playable final int x = owner.getX(); final int y = owner.getY(); final int z = owner.getZ(); - final Location location = GeoEngine.getInstance().canMoveToTargetLoc(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, owner.getInstanceWorld()); + final Location location = GeoEngine.getInstance().getValidLocation(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, getInstanceWorld()); setXYZInvisible(location.getX(), location.getY(), location.getZ()); } diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java index abaec9036f..20cd1966d5 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java @@ -1560,7 +1560,7 @@ public class ItemInstance extends WorldObject if (dropper != null) { final Instance instance = dropper.getInstanceWorld(); - final Location dropDest = GeoEngine.getInstance().canMoveToTargetLoc(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); + final Location dropDest = GeoEngine.getInstance().getValidLocation(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); x = dropDest.getX(); y = dropDest.getY(); z = dropDest.getZ(); diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java index 5d294fe47c..558bf92edf 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java @@ -1179,7 +1179,7 @@ public class SkillCaster implements Runnable } } - final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().canMoveToTargetLoc(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); + final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().getValidLocation(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); creature.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); creature.broadcastPacket(new FlyToLocation(creature, destination, flyType, 0, 0, 333)); diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java index c62c821f44..30068134e3 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java @@ -114,7 +114,7 @@ public class ValidatePosition implements IClientIncomingPacket { if (player.isFalling(_z)) { - final int nearestZ = GeoEngine.getInstance().getHigherHeight(_x, _y, _z); + final int nearestZ = GeoEngine.getInstance().getHeight(_x, _y, _z); if (player.getZ() < nearestZ) { player.setXYZ(_x, _y, nearestZ); diff --git a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/util/GeoUtils.java index 4600a0fc53..19a979819e 100644 --- a/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_Classic_2.4_SecretOfEmpire/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -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.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; @@ -30,21 +30,21 @@ public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getInstance().getWorldX(iter.x()); - final int wy = GeoEngine.getInstance().getWorldY(iter.y()); + final int wx = GeoEngine.getWorldX(iter.x()); + final int wy = GeoEngine.getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public final class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); - int wy = GeoEngine.getInstance().getWorldY(prevY); + int wx = GeoEngine.getWorldX(prevX); + int wy = GeoEngine.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.getInstance().getWorldX(curX); - wy = GeoEngine.getInstance().getWorldY(curY); + wx = GeoEngine.getWorldX(curX); + wy = GeoEngine.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().checkNearestNswe(x, y, z, nswe)) + if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0) { return Color.GREEN; } @@ -109,9 +109,8 @@ public final class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final GeoEngine ge = GeoEngine.getInstance(); - final int playerGx = ge.getGeoX(player.getX()); - final int playerGy = ge.getGeoY(player.getY()); + final int playerGx = GeoEngine.getGeoX(player.getX()); + final int playerGy = GeoEngine.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -135,32 +134,32 @@ public final class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = ge.getWorldX(gx); - final int y = ge.getWorldY(gy); - final int z = ge.getNearestZ(gx, gy, player.getZ()); + final int x = GeoEngine.getWorldX(gx); + final int y = GeoEngine.getWorldY(gy); + final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); + Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); 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, Cell.NSWE_EAST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); 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, Cell.NSWE_SOUTH); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); 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, Cell.NSWE_WEST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); 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); @@ -188,41 +187,41 @@ public final class GeoUtils { if (y > lastY) { - return Cell.NSWE_SOUTH_EAST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E; } else if (y < lastY) { - return Cell.NSWE_NORTH_EAST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E; } else { - return Cell.NSWE_EAST; + return GeoStructure.CELL_FLAG_E; } } else if (x < lastX) // west { if (y > lastY) { - return Cell.NSWE_SOUTH_WEST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W; } else if (y < lastY) { - return Cell.NSWE_NORTH_WEST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W; } else { - return Cell.NSWE_WEST; + return GeoStructure.CELL_FLAG_W; } } else // unchanged x { if (y > lastY) { - return Cell.NSWE_SOUTH; + return GeoStructure.CELL_FLAG_S; } else if (y < lastY) { - return Cell.NSWE_NORTH; + return GeoStructure.CELL_FLAG_N; } else { diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/config/GeoEngine.ini b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/config/GeoEngine.ini index 609e8a89a7..e740c678dd 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/config/GeoEngine.ini @@ -6,33 +6,44 @@ # at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ 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 + # ================================================================= -# Path finding +# Pathfinding # ================================================================= # 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 +# an alternative path (e.g. walk around obstacle), default: True +PathFinding = True -# Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 -PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 +# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 -# Weight for nodes without obstacles far from walls -LowWeight = 0.5 +# Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 +MoveWeight = 10 +MoveWeightDiag = 14 -# Weight for nodes near walls -MediumWeight = 2 +# 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 -# Weight for nodes with obstacles -HighWeight = 3 +# 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. -# Default: LowWeight * sqrt(2) -DiagonalWeight = 0.707 +# Maximum number of generated nodes per one path-finding process, default 3500 +MaxIterations = 3500 # ================================================================= -# Other +# Line of Sight # ================================================================= -# Correct player Z after falling through the ground. -CorrectPlayerZ = False +# 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 diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/geodata/L2D_GeoEngine.patch b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/geodata/L2D_GeoEngine.patch deleted file mode 100644 index f8577e3a0e..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/geodata/L2D_GeoEngine.patch +++ /dev/null @@ -1,6035 +0,0 @@ -Index: dist/game/GeoDataConverter.bat -=================================================================== ---- dist/game/GeoDataConverter.bat (nonexistent) -+++ dist/game/GeoDataConverter.bat (working copy) -@@ -0,0 +1,6 @@ -+@echo off -+title L2D geodata converter -+ -+java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter -+ -+pause -Index: dist/game/GeoDataConverter.sh -=================================================================== ---- dist/game/GeoDataConverter.sh (nonexistent) -+++ dist/game/GeoDataConverter.sh (working copy) -@@ -0,0 +1,4 @@ -+#! /bin/sh -+ -+java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 -+ -Index: dist/game/config/GeoEngine.ini -=================================================================== ---- dist/game/config/GeoEngine.ini (revision 8409) -+++ dist/game/config/GeoEngine.ini (working copy) -@@ -1,6 +1,10 @@ - # ================================================================= - # Geodata - # ================================================================= -+# Because of real-time performance we are using geodata files only in -+# diagonal L2D format now (using filename e.g. 22_16.l2d). -+# L2D geodata can be obtained by conversion of existing L2J or L2OFF geodata. -+# Launch "GeoDataConverter.bat/sh" and follow instructions to start the conversion. - - # 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/ -@@ -13,6 +17,16 @@ - CoordSynchronize = 2 - - # ================================================================= -+# Path checking -+# ================================================================= -+ -+# 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 finding - # ================================================================= - -@@ -23,19 +37,23 @@ - # Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - --# Weight for nodes without obstacles far from walls --LowWeight = 0.5 -+# Base path weight, when moving from one node to another on axis direction, default: 10 -+BaseWeight = 10 - --# Weight for nodes near walls --MediumWeight = 2 -+# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -+DiagonalWeight = 14 - --# Weight for nodes with obstacles --HighWeight = 3 -+# When movement flags of target node is blocked to any direction, multiply movement weight by this multiplier. -+# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 10 -+ObstacleMultiplier = 10 - --# Weight for diagonal movement. --# Default: LowWeight * sqrt(2) --DiagonalWeight = 0.707 -+# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 20 -+# For proper function must be higher than BaseWeight and/or DiagonalWeight. -+HeuristicWeight = 20 - -+# Maximum number of generated nodes per one path-finding process, default 3500 -+MaxIterations = 3500 -+ - # ================================================================= - # Other - # ================================================================= -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (working copy) -@@ -55,9 +55,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -@@ -73,9 +72,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (working copy) -@@ -18,11 +18,11 @@ - - import java.util.List; - --import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -+import org.l2jmobius.gameserver.geoengine.GeoEngine; - import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -+import org.l2jmobius.gameserver.network.SystemMessageId; - import org.l2jmobius.gameserver.util.BuilderUtil; - - public class AdminPathNode implements IAdminCommandHandler -@@ -37,29 +37,30 @@ - { - if (command.equals("admin_path_find")) - { -- if (!Config.PATHFINDING) -- { -- BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); -- return true; -- } - if (activeChar.getTarget() != null) - { -- final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); -+ final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); - if (path == null) - { -- BuilderUtil.sendSysMessage(activeChar, "No Route!"); -- return true; -+ BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); - } -- for (AbstractNodeLoc a : path) -+ else - { -- BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); -+ for (Location point : path) -+ { -+ BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); -+ } - } - } - else - { -- BuilderUtil.sendSysMessage(activeChar, "No Target!"); -+ activeChar.sendPacket(SystemMessageId.INVALID_TARGET); - } - } -+ else -+ { -+ return false; -+ } - return true; - } - -Index: java/org/l2jmobius/Config.java -=================================================================== ---- java/org/l2jmobius/Config.java (revision 8409) -+++ java/org/l2jmobius/Config.java (working copy) -@@ -32,7 +32,6 @@ - import java.net.UnknownHostException; - import java.nio.charset.StandardCharsets; - import java.nio.file.Files; --import java.nio.file.Path; - import java.nio.file.Paths; - import java.time.Duration; - import java.util.ArrayList; -@@ -966,14 +965,17 @@ - // -------------------------------------------------- - // GeoEngine - // -------------------------------------------------- -- public static Path GEODATA_PATH; -+ public static String GEODATA_PATH; -+ public static int COORD_SYNCHRONIZE; -+ public static int PART_OF_CHARACTER_HEIGHT; -+ public static int MAX_OBSTACLE_HEIGHT; - public static boolean PATHFINDING; - public static String PATHFIND_BUFFERS; -- public static float LOW_WEIGHT; -- public static float MEDIUM_WEIGHT; -- public static float HIGH_WEIGHT; -- public static float DIAGONAL_WEIGHT; -- public static int COORD_SYNCHRONIZE; -+ public static int BASE_WEIGHT; -+ public static int DIAGONAL_WEIGHT; -+ public static int HEURISTIC_WEIGHT; -+ public static int OBSTACLE_MULTIPLIER; -+ public static int MAX_ITERATIONS; - public static boolean CORRECT_PLAYER_Z; - - /** Attribute System */ -@@ -2568,14 +2570,17 @@ - - // Load GeoEngine config file (if exists) - final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); -- GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); -+ GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); -+ COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); -+ MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); - PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -- LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); -- MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); -- HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); -- DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); -- COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); -+ DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); -+ OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); -+ HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); -+ MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); - CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); - - // Load AllowedPlayerRaces config file (if exists) -Index: java/org/l2jmobius/gameserver/geoengine/GeoEngine.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (revision 8435) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (working copy) -@@ -16,100 +16,90 @@ - */ - package org.l2jmobius.gameserver.geoengine; - --import java.io.IOException; -+import java.io.File; - import java.io.RandomAccessFile; - import java.nio.ByteOrder; --import java.nio.channels.FileChannel.MapMode; --import java.nio.file.Files; --import java.nio.file.Path; --import java.util.concurrent.atomic.AtomicReferenceArray; --import java.util.logging.Level; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.List; - import java.util.logging.Logger; - - import org.l2jmobius.Config; - import org.l2jmobius.gameserver.data.xml.DoorData; - import org.l2jmobius.gameserver.data.xml.FenceData; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; --import org.l2jmobius.gameserver.geoengine.geodata.IRegion; --import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; --import org.l2jmobius.gameserver.geoengine.geodata.Region; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.World; - import org.l2jmobius.gameserver.model.WorldObject; -+import org.l2jmobius.gameserver.model.actor.Creature; - import org.l2jmobius.gameserver.model.instancezone.Instance; --import org.l2jmobius.gameserver.model.interfaces.ILocational; --import org.l2jmobius.gameserver.util.GeoUtils; --import org.l2jmobius.gameserver.util.LinePointIterator; --import org.l2jmobius.gameserver.util.LinePointIterator3D; -+import org.l2jmobius.gameserver.util.MathUtil; - - /** -- * @author -Nemesiss-, HorridoJoho -+ * @author Hasha - */ - public class GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); -+ protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - -- private static final int WORLD_MIN_X = -655360; -- private static final int WORLD_MIN_Y = -589824; -- private static final int WORLD_MIN_Z = -16384; -+ private final ABlock[][] _blocks; -+ private final BlockNull _nullBlock; - -- /** 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; -- -- /** The regions array */ -- private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); -- -- private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; -- private static final int ELEVATED_SEE_OVER_DISTANCE = 2; -- private static final int MAX_SEE_OVER_HEIGHT = 48; -- -- protected GeoEngine() -+ /** -+ * GeoEngine constructor. Loads all geodata files of chosen geodata format. -+ */ -+ public GeoEngine() - { - LOGGER.info("GeoEngine: Initializing..."); -- for (int i = 0; i < _regions.length(); i++) -- { -- _regions.set(i, NullRegion.INSTANCE); -- } - -+ // initialize block container -+ _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; -+ -+ // load null block -+ _nullBlock = new BlockNull(); -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files according to geoengine config setup - int loaded = 0; -- try -+ long fileSize = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { -- for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { -- for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) -+ final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); -+ if (f.exists() && !f.isDirectory()) - { -- final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); -- if (Files.exists(geoFilePath)) -+ // region file is load-able, try to load it -+ if (loadGeoBlocks(rx, ry)) - { -- try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) -- { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -- loaded++; -- } -+ loaded++; -+ fileSize += f.length(); - } - } -+ else -+ { -+ // region file is not load-able, load null blocks -+ loadNullBlocks(rx, ry); -+ } - } - } -- catch (Exception e) -+ -+ LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -+ if (loaded > 0) - { -- LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); -- System.exit(1); -+ LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); - } - -- LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -- -- // Avoid wrong configs when no files are loaded. -+ // avoid wrong configs when no files are loaded - if (loaded == 0) - { - if (Config.PATHFINDING) -@@ -123,619 +113,820 @@ - LOGGER.info("GeoEngine: Forcing CoordSynchronize setting to -1."); - } - } -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); - } - - /** -- * @param geoX -- * @param geoY -- * @return the region -+ * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. -+ * @return boolean : True, when geodata file was loaded without problem. - */ -- private IRegion getRegion(int geoX, int geoY) -+ private final boolean loadGeoBlocks(int regionX, int regionY) - { -- final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); -- if ((region < 0) || (region >= _regions.length())) -+ final String filename = String.format(GeoFormat.L2D.getFilename(), regionX, regionY); -+ -+ // standard load -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) - { -- return null; -+ // initialize file buffer -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, GeoFormat.L2D); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ } -+ -+ // check data consistency -+ if (buffer.remaining() > 0) -+ { -+ LOGGER.warning("GeoEngine: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ } -+ -+ // loading was successful -+ return true; - } -- return _regions.get(region); -- } -- -- /** -- * @param filePath -- * @param regionX -- * @param regionY -- * @throws IOException -- */ -- 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")) -+ catch (Exception e) - { -- _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -+ // an error occured while loading, load null blocks -+ LOGGER.warning("GeoEngine: Error while loading " + filename + " region file."); -+ LOGGER.warning(e.getMessage()); -+ -+ // replace whole region file with null blocks -+ loadNullBlocks(regionX, regionY); -+ -+ // loading was not successful -+ return false; - } - } - - /** -- * @param regionX -- * @param regionY -+ * Loads null blocks. Used when no region file is detected or an error occurs during loading. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. - */ -- public void unloadRegion(int regionX, int regionY) -+ private final void loadNullBlocks(int regionX, int regionY) - { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); -- } -- -- /** -- * @param geoX -- * @param geoY -- * @return if geodata exist -- */ -- public boolean hasGeoPos(int geoX, int geoY) -- { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // load all null blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { -- return false; -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[blockX + ix][blockY + iy] = _nullBlock; -+ } - } -- return region.hasGeo(); - } - -+ // GEODATA - GENERAL -+ - /** -- * Checks the specified position for available geodata. -- * @param x the world x -- * @param y the world y -- * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise -+ * Converts world X to geodata X. -+ * @param worldX -+ * @return int : Geo X - */ -- public boolean hasGeo(int x, int y) -+ public static int getGeoX(int worldX) - { -- return hasGeoPos(getGeoX(x), getGeoY(y)); -+ return (MathUtil.limit(worldX, World.MAP_MIN_X, World.MAP_MAX_X) - World.MAP_MIN_X) >> 4; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe check -+ * Converts world Y to geodata Y. -+ * @param worldY -+ * @return int : Geo Y - */ -- public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) -+ public static int getGeoY(int worldY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return true; -- } -- return region.checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(worldY, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.MAP_MIN_Y) >> 4; - } - - /** -+ * Converts geodata X to world X. - * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe anti-corner cut -+ * @return int : World X - */ -- public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) -+ public static int getWorldX(int geoX) - { -- boolean can = true; -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- return can && checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(geoX, 0, GeoStructure.GEO_CELLS_X) << 4) + World.MAP_MIN_X + 8; - } - - /** -- * @param geoX -+ * Converts geodata Y to world Y. - * @param geoY -- * @param worldZ -- * @return the nearest Z value -+ * @return int : World Y - */ -- public int getNearestZ(int geoX, int geoY, int worldZ) -+ public static int getWorldY(int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return worldZ; -- } -- return region.getNearestZ(geoX, geoY, worldZ); -+ return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next lower Z value -+ * Returns block of geodata on given coordinates. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return {@link ABlock} : Block of geodata. - */ -- public int getNextLowerZ(int geoX, int geoY, int worldZ) -+ private final ABlock getBlock(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final int x = geoX / GeoStructure.BLOCK_CELLS_X; -+ final int y = geoY / GeoStructure.BLOCK_CELLS_Y; -+ -+ // if x or y is out of array return null -+ if ((x > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) - { -- return worldZ; -+ return _blocks[x][y]; - } -- return region.getNextLowerZ(geoX, geoY, worldZ); -+ return null; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next higher Z value -+ * Check if geo coordinates has geo. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return boolean : True, if given geo coordinates have geodata - */ -- public int getNextHigherZ(int geoX, int geoY, int worldZ) -+ public boolean hasGeoPos(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final ABlock block = getBlock(geoX, geoY); -+ if (block == null) // null block check - { -- return worldZ; -+ // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) -+ // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); -+ return false; - } -- return region.getNextHigherZ(geoX, geoY, worldZ); -+ return block.hasGeoPos(); - } - - /** -- * Gets the Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the height of cell, which is closest to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, closest to given coordinates. - */ -- public int getHeight(int x, int y, int z) -+ public short getHeightNearest(int geoX, int geoY, int worldZ) - { -- return getNearestZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getHeightNearest(geoX, geoY, worldZ) : (short) worldZ; - } - - /** -- * Gets the next lower Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the NSWE flag byte of cell, which is closes to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. - */ -- public int getLowerHeight(int x, int y, int z) -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) - { -- return getNextLowerZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getNsweNearest(geoX, geoY, worldZ) : (byte) 0xFF; - } - - /** -- * Gets the next higher Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Check if world coordinates has geo. -+ * @param worldX : World X -+ * @param worldY : World Y -+ * @return boolean : True, if given world coordinates have geodata - */ -- public int getHigherHeight(int x, int y, int z) -+ public boolean hasGeo(int worldX, int worldY) - { -- return getNextHigherZ(getGeoX(x), getGeoY(y), z); -+ return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); - } - - /** -- * @param worldX -- * @return the geo X -+ * Returns closest Z coordinate according to geodata. -+ * @param worldX : world x -+ * @param worldY : world y -+ * @param worldZ : world z -+ * @return short : nearest Z coordinates according to geodata - */ -- public int getGeoX(int worldX) -+ public short getHeight(int worldX, int worldY, int worldZ) - { -- return (worldX - WORLD_MIN_X) / 16; -+ return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - -- /** -- * @param worldY -- * @return the geo Y -- */ -- public int getGeoY(int worldY) -- { -- return (worldY - WORLD_MIN_Y) / 16; -- } -+ // PATHFINDING - - /** -- * @param worldZ -- * @return the geo Z -+ * Check line of sight from {@link WorldObject} to {@link WorldObject}. -+ * @param origin : The origin object. -+ * @param target : The target object. -+ * @return {@code boolean} : True if origin can see target - */ -- public int getGeoZ(int worldZ) -+ public boolean canSeeTarget(WorldObject origin, WorldObject target) - { -- return (worldZ - WORLD_MIN_Z) / 16; -- } -- -- /** -- * @param geoX -- * @return the world X -- */ -- public int getWorldX(int geoX) -- { -- return (geoX * 16) + WORLD_MIN_X + 8; -- } -- -- /** -- * @param geoY -- * @return the world Y -- */ -- public int getWorldY(int geoY) -- { -- return (geoY * 16) + WORLD_MIN_Y + 8; -- } -- -- /** -- * @param geoZ -- * @return the world Z -- */ -- public int getWorldZ(int geoZ) -- { -- return (geoZ * 16) + WORLD_MIN_Z + 8; -- } -- -- /** -- * Can see target. Doors as target always return true. Checks doors between. -- * @param cha the character -- * @param target the target -- * @return {@code true} if the character can see the target (LOS), {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, WorldObject target) -- { -- if (target.isDoor()) -+ if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) - { -- // Can always see doors. - return true; - } -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param cha the character -- * @param worldPosition the world position -- * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) -- { -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param world -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @param tworld the target's instanceId -- * @return -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) -- { -- return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instance -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) -- { -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) -+ -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = target.getX(); -+ final int ty = target.getY(); -+ final int tz = target.getZ(); -+ -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } -- return canSeeTarget(x, y, z, tx, ty, tz); -- } -- -- /** -- * @param prevX -- * @param prevY -- * @param prevGeoZ -- * @param curX -- * @param curY -- * @param nswe -- * @return the LOS Z value -- */ -- private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) -- { -- if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { -- throw new RuntimeException("Multiple directions!"); -+ return false; - } - -- if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- return getNearestZ(curX, curY, prevGeoZ); -+ return true; - } - -- return getNextHigherZ(curX, curY, prevGeoZ); -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) -+ { -+ return true; -+ } -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) -+ { -+ return goz == gtz; -+ } -+ -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) -+ { -+ oheight = ((Creature) origin).getCollisionHeight() * 2; -+ } -+ -+ double theight = 0; -+ if (target.isCreature()) -+ { -+ theight = ((Creature) target).getCollisionHeight() * 2; -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); - } - - /** -- * Can see target. Does not check doors between. -- * @param xValue the x coordinate -- * @param yValue the y coordinate -- * @param zValue the z coordinate -- * @param txValue the target's x coordinate -- * @param tyValue the target's y coordinate -- * @param tzValue the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -+ * Check line of sight from {@link WorldObject} to {@link Location}. -+ * @param origin : The origin object. -+ * @param position : The target position. -+ * @return {@code boolean} : True if object can see position - */ -- public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) -+ public boolean canSeeTarget(WorldObject origin, Location position) - { -- int x = xValue; -- int y = yValue; -- int tx = txValue; -- int ty = tyValue; -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = position.getX(); -+ final int ty = position.getY(); -+ final int tz = position.getZ(); - -- int geoX = getGeoX(x); -- int geoY = getGeoY(y); -- int tGeoX = getGeoX(tx); -- int tGeoY = getGeoY(ty); -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) -+ { -+ return false; -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) -+ { -+ return false; -+ } - -- int z = getNearestZ(geoX, geoY, zValue); -- int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- // Fastpath. -- if ((geoX == tGeoX) && (geoY == tGeoY)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- if (hasGeoPos(tGeoX, tGeoY)) -- { -- return z == tz; -- } - return true; - } - -- if (tz > z) -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) - { -- int tmp = tx; -- tx = x; -- x = tmp; -- -- tmp = ty; -- ty = y; -- y = tmp; -- -- tmp = tz; -- tz = z; -- z = tmp; -- -- tmp = tGeoX; -- tGeoX = geoX; -- geoX = tmp; -- -- tmp = tGeoY; -- tGeoY = geoY; -- geoY = tmp; -+ return goz == gtz; - } - -- final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); -- // First point is guaranteed to be available, skip it, we can always see our own position. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- final int prevZ = pointIter.z(); -- int prevGeoZ = prevZ; -- int ptIndex = 0; -- while (pointIter.next()) -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight(); -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); -+ } -+ -+ /** -+ * Simple check for origin to target visibility. -+ * @param goxValue : origin X geodata coordinate -+ * @param goyValue : origin Y geodata coordinate -+ * @param gozValue : origin Z geodata coordinate -+ * @param oheight : origin height (if instance of {@link Character}) -+ * @param gtxValue : target X geodata coordinate -+ * @param gtyValue : target Y geodata coordinate -+ * @param gtzValue : target Z geodata coordinate -+ * @param theight : target height (if instance of {@link Character}) -+ * @param instance -+ * @return {@code boolean} : True, when target can be seen. -+ */ -+ private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) -+ { -+ int goz = gozValue; -+ int gtz = gtzValue; -+ int gox = goxValue; -+ int goy = goyValue; -+ int gtx = gtxValue; -+ int gty = gtyValue; -+ -+ // get line of sight Z coordinates -+ double losoz = goz + ((oheight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ double lostz = gtz + ((theight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ -+ // get X delta and signum -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirox = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; -+ final byte dirtx = sx > 0 ? GeoStructure.CELL_FLAG_W : GeoStructure.CELL_FLAG_E; -+ -+ // get Y delta and signum -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte diroy = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ final byte dirty = sy > 0 ? GeoStructure.CELL_FLAG_N : GeoStructure.CELL_FLAG_S; -+ -+ // get Z delta -+ final int dm = Math.max(dx, dy); -+ final double dz = (lostz - losoz) / dm; -+ -+ // get direction flag for diagonal movement -+ final byte diroxy = getDirXY(dirox, diroy); -+ final byte dirtxy = getDirXY(dirtx, dirty); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte diro; -+ byte dirt; -+ -+ // initialize node values -+ int nox = gox; -+ int noy = goy; -+ int ntx = gtx; -+ int nty = gty; -+ byte nsweo = getNsweNearest(gox, goy, goz); -+ byte nswet = getNsweNearest(gtx, gty, gtz); -+ -+ // loop -+ ABlock block; -+ int index; -+ for (int i = 0; i < ((dm + 1) / 2); i++) -+ { -+ // reset direction flag -+ diro = 0; -+ dirt = 0; - -- if ((curX == prevX) && (curY == prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- continue; -+ // calculate next point XY coordinates -+ d -= dy; -+ d += dx; -+ nox += sx; -+ ntx -= sx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroxy; -+ dirt |= dirtxy; - } -+ else if (e2 > -dy) -+ { -+ // calculate next point X coordinate -+ d -= dy; -+ nox += sx; -+ ntx -= sx; -+ diro |= dirox; -+ dirt |= dirtx; -+ } -+ else if (e2 < dx) -+ { -+ // calculate next point Y coordinate -+ d += dx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroy; -+ dirt |= dirty; -+ } - -- final int beeCurZ = pointIter.z(); -- int curGeoZ = prevGeoZ; -- -- // Check if the position has geodata. -- if (hasGeoPos(curX, curY)) - { -- final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); -- curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); -- int maxHeight; -- if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) -+ // get block of the next cell -+ block = getBlock(nox, noy); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nsweo & diro) == 0) - { -- maxHeight = z + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); - } - else - { -- maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); - } - -- boolean canSeeThrough = false; -- if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) -+ // layer does not exist, return -+ if (index == -1) - { -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else -- { -- canSeeThrough = true; -- } -+ return false; - } - -- if (!canSeeThrough) -+ // get layer and next line of sight Z coordinate -+ goz = block.getHeight(index); -+ losoz += dz; -+ -+ // perform line of sight check, return when fails -+ if ((goz - losoz) > Config.MAX_OBSTACLE_HEIGHT) - { - return false; - } -+ -+ // get layer nswe -+ nsweo = block.getNswe(index); - } -+ { -+ // get block of the next cell -+ block = getBlock(ntx, nty); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nswet & dirt) == 0) -+ { -+ index = block.getIndexAbove(ntx, nty, gtz - GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ else -+ { -+ index = block.getIndexBelow(ntx, nty, gtz + GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ -+ // layer does not exist, return -+ if (index == -1) -+ { -+ return false; -+ } -+ -+ // get layer and next line of sight Z coordinate -+ gtz = block.getHeight(index); -+ lostz -= dz; -+ -+ // perform line of sight check, return when fails -+ if ((gtz - lostz) > Config.MAX_OBSTACLE_HEIGHT) -+ { -+ return false; -+ } -+ -+ // get layer nswe -+ nswet = block.getNswe(index); -+ } - -- prevX = curX; -- prevY = curY; -- prevGeoZ = curGeoZ; -- ++ptIndex; -+ // update coords -+ gox = nox; -+ goy = noy; -+ gtx = ntx; -+ gty = nty; - } - -- return true; -+ // when iteration is completed, compare final Z coordinates -+ return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); - } - - /** -- * Move check. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param zValue the z coordinate -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tzValue the target's z coordinate -- * @param instance the instance -- * @return the last Location (x,y,z) where player can walk - just before wall -+ * Check movement from coordinates to coordinates. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {code boolean} : True if target coordinates are reachable from origin coordinates - */ -- public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) -+ public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- final int geoX = getGeoX(x); -- final int geoY = getGeoY(y); -- final int z = getNearestZ(geoX, geoY, zValue); -- final int tGeoX = getGeoX(tx); -- final int tGeoY = getGeoY(ty); -- final int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } -- if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = z; -+ // perform geodata check -+ final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); -+ return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); -+ } -+ -+ /** -+ * Check movement from origin to target. Returns last available point in the checked path. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {@link Location} : Last point where object can walk (just before wall) -+ */ -+ public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) -+ { -+ return new Location(ox, oy, oz); -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) -+ { -+ return new Location(ox, oy, oz); -+ } - -- while (pointIter.next()) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -- -- if (hasGeoPos(prevX, prevY)) -- { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -- { -- // Can't move, return previous location. -- return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); -- } -- } -- -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return new Location(tx, ty, tz); - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != tz)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- // Different floors, return start location. -- return new Location(x, y, z); -+ return new Location(tx, ty, tz); - } - -- return new Location(tx, ty, tz); -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) -+ { -+ return new Location(tx, ty, tz); -+ } -+ -+ // perform geodata check -+ return checkMove(gox, goy, goz, gtx, gty, gtz, instance); - } - - /** -- * Checks if its possible to move from one location to another. -- * @param fromX the X coordinate to start checking from -- * @param fromY the Y coordinate to start checking from -- * @param fromZvalue the Z coordinate to start checking from -- * @param toX the X coordinate to end checking at -- * @param toY the Y coordinate to end checking at -- * @param toZvalue the Z coordinate to end checking at -- * @param instance the instance -- * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise -+ * With this method you can check if a position is visible or can be reached by beeline movement.
-+ * Target X and Y reachable and Z is on same floor: -+ *
    -+ *
  • Location of the target with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y reachable but Z is on another floor: -+ *
    -+ *
  • Location of the origin with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y not reachable: -+ *
    -+ *
  • Last accessible location in destination to target.
  • -+ *
-+ * @param gox : origin X geodata coordinate -+ * @param goy : origin Y geodata coordinate -+ * @param goz : origin Z geodata coordinate -+ * @param gtx : target X geodata coordinate -+ * @param gty : target Y geodata coordinate -+ * @param gtz : target Z geodata coordinate -+ * @param instance -+ * @return {@link GeoLocation} : The last allowed point of movement. - */ -- public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) -+ protected final GeoLocation checkMove(int gox, int goy, int goz, int gtx, int gty, int gtz, Instance instance) - { -- final int geoX = getGeoX(fromX); -- final int geoY = getGeoY(fromY); -- final int fromZ = getNearestZ(geoX, geoY, fromZvalue); -- final int tGeoX = getGeoX(toX); -- final int tGeoY = getGeoY(toY); -- final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); -- -- if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) -+ if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } -- if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) -+ if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = fromZ; -+ // get X delta, signum and direction flag -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirX = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; - -- while (pointIter.next()) -+ // get Y delta, signum and direction flag -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte dirY = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ -+ // get direction flag for diagonal movement -+ final byte dirXY = getDirXY(dirX, dirY); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte direction; -+ -+ // load pointer coordinates -+ int gpx = gox; -+ int gpy = goy; -+ int gpz = goz; -+ -+ // load next pointer -+ int nx = gpx; -+ int ny = gpy; -+ -+ // loop -+ int count = 0; -+ while (count++ < Config.MAX_ITERATIONS) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -+ direction = 0; - -- if (hasGeoPos(prevX, prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -+ d -= dy; -+ d += dx; -+ nx += sx; -+ ny += sy; -+ direction |= dirXY; -+ } -+ else if (e2 > -dy) -+ { -+ d -= dy; -+ nx += sx; -+ direction |= dirX; -+ } -+ else if (e2 < dx) -+ { -+ d += dx; -+ ny += sy; -+ direction |= dirY; -+ } -+ -+ // obstacle found, return -+ if ((getNsweNearest(gpx, gpy, gpz) & direction) == 0) -+ { -+ return new GeoLocation(gpx, gpy, gpz); -+ } -+ -+ // update pointer coordinates -+ gpx = nx; -+ gpy = ny; -+ gpz = getHeightNearest(nx, ny, gpz); -+ -+ // target coordinates reached -+ if ((gpx == gtx) && (gpy == gty)) -+ { -+ if (gpz == gtz) - { -- return false; -+ // path found, Z coordinates are okay, return target point -+ return new GeoLocation(gtx, gty, gtz); - } -+ -+ // path found, Z coordinates are not okay, return last good point -+ return new GeoLocation(gpx, gpy, gpz); - } -+ } -+ -+ return new GeoLocation(gox, goy, goz); -+ } -+ -+ /** -+ * Returns diagonal NSWE flag format of combined two NSWE flags. -+ * @param dirX : X direction NSWE flag -+ * @param dirY : Y direction NSWE flag -+ * @return byte : NSWE flag of combined direction -+ */ -+ private static byte getDirXY(byte dirX, byte dirY) -+ { -+ // check axis directions -+ if (dirY == GeoStructure.CELL_FLAG_N) -+ { -+ if (dirX == GeoStructure.CELL_FLAG_W) -+ { -+ return GeoStructure.CELL_FLAG_NW; -+ } - -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return GeoStructure.CELL_FLAG_NE; - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) -+ if (dirX == GeoStructure.CELL_FLAG_W) - { -- // Different floors. -- return false; -+ return GeoStructure.CELL_FLAG_SW; - } - -- return true; -+ return GeoStructure.CELL_FLAG_SE; - } - -+ /** -+ * Returns the list of location objects as a result of complete path calculation. -+ * @param ox : origin x -+ * @param oy : origin y -+ * @param oz : origin z -+ * @param tx : target x -+ * @param ty : target y -+ * @param tz : target z -+ * @param instance -+ * @return {@code List} : complete path from nodes -+ */ -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ return null; -+ } -+ -+ /** -+ * Returns the instance of the {@link GeoEngine}. -+ * @return {@link GeoEngine} : The instance. -+ */ - public static GeoEngine getInstance() - { - return SingletonHolder.INSTANCE; -@@ -743,6 +934,6 @@ - - private static class SingletonHolder - { -- protected static final GeoEngine INSTANCE = new GeoEngine(); -+ protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); - } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (working copy) -@@ -20,88 +20,84 @@ - import java.util.LinkedList; - import java.util.List; - import java.util.ListIterator; --import java.util.logging.Level; --import java.util.logging.Logger; - - import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; --import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; --import org.l2jmobius.gameserver.model.World; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -+import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.instancezone.Instance; - - /** -- * @author -Nemesiss- -+ * @author Hasha - */ --public class GeoEnginePathfinding -+final class GeoEnginePathfinding extends GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); -+ // pre-allocated buffers -+ private final BufferHolder[] _buffers; - -- private BufferInfo[] _buffers; -- - protected GeoEnginePathfinding() - { -- try -+ super(); -+ -+ final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ _buffers = new BufferHolder[array.length]; -+ int count = 0; -+ for (int i = 0; i < array.length; i++) - { -- final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ final String buf = array[i]; -+ final String[] args = buf.split("x"); - -- _buffers = new BufferInfo[array.length]; -- -- String buf; -- String[] args; -- for (int i = 0; i < array.length; i++) -+ try - { -- buf = array[i]; -- args = buf.split("x"); -- if (args.length != 2) -- { -- throw new Exception("Invalid buffer definition: " + buf); -- } -- -- _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); -+ final int size = Integer.parseInt(args[1]); -+ count += size; -+ _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); - } -+ catch (Exception e) -+ { -+ LOGGER.warning("GeoEnginePathfinding: Can not load buffer setting: " + buf); -+ } - } -- catch (Exception e) -- { -- LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); -- throw new Error("CellPathFinding: load aborted"); -- } -+ -+ LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); - } - -- public boolean pathNodesExist(short regionoffset) -+ @Override -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- return false; -- } -- -- public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) -- { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -- if (!GeoEngine.getInstance().hasGeo(x, y)) -+ // get origin and check existing geo coords -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { - 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)) -+ -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coords -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { - 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)))); -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // Prepare buffer for pathfinding calculations -+ final NodeBuffer buffer = getBuffer(64 + (2 * Math.max(Math.abs(gox - gtx), Math.abs(goy - gty)))); - if (buffer == null) - { - return null; - } - -- List path = null; -+ // find path -+ List path = null; - try - { -- final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); -- -+ final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); - if (result == null) - { - return null; -@@ -125,33 +121,45 @@ - return path; - } - -- int currentX, currentY, currentZ; -- ListIterator middlePoint; -+ // get path list iterator -+ final ListIterator point = path.listIterator(); - -- middlePoint = path.listIterator(); -- currentX = x; -- currentY = y; -- currentZ = z; -+ // get node A (origin) -+ int nodeAx = gox; -+ int nodeAy = goy; -+ short nodeAz = goz; - -- while (middlePoint.hasNext()) -+ // get node B -+ GeoLocation nodeB = (GeoLocation) point.next(); -+ -+ // iterate thought the path to optimize it -+ int count = 0; -+ while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) - { -- final AbstractNodeLoc locMiddle = middlePoint.next(); -- if (!middlePoint.hasNext()) -- { -- break; -- } -+ // get node C -+ final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - -- final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); -- if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) -+ // check movement from node A to node C -+ final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); -+ if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) - { -- middlePoint.remove(); -+ // can move from node A to node C -+ -+ // remove node B -+ point.remove(); - } - else - { -- currentX = locMiddle.getX(); -- currentY = locMiddle.getY(); -- currentZ = locMiddle.getZ(); -+ // can not move from node A to node C -+ -+ // set node A (node B is part of path, update A coordinates) -+ nodeAx = nodeB.getGeoX(); -+ nodeAy = nodeB.getGeoY(); -+ nodeAz = (short) nodeB.getZ(); - } -+ -+ // set node B -+ nodeB = (GeoLocation) point.next(); - } - - return path; -@@ -158,144 +166,102 @@ - } - - /** -- * Convert geodata position to pathnode position -- * @param geo_pos -- * @return pathnode position -+ * Create list of node locations as result of calculated buffer node tree. -+ * @param node : the entry point -+ * @return List : list of node location - */ -- public short getNodePos(int geo_pos) -+ private static List constructPath(Node node) - { -- return (short) (geo_pos >> 3); // OK? -- } -- -- /** -- * Convert node position to pathnode block position -- * @param node_pos -- * @return pathnode block position (0...255) -- */ -- public short getNodeBlock(int node_pos) -- { -- return (short) (node_pos % 256); -- } -- -- public byte getRegionX(int node_pos) -- { -- return (byte) ((node_pos >> 8) + World.TILE_X_MIN); -- } -- -- public byte getRegionY(int node_pos) -- { -- return (byte) ((node_pos >> 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 node_x rx -- * @return -- */ -- public int calculateWorldX(short node_x) -- { -- return World.MAP_MIN_X + (node_x * 128) + 48; -- } -- -- /** -- * Convert pathnode y to World y position -- * @param node_y -- * @return -- */ -- public int calculateWorldY(short node_y) -- { -- return World.MAP_MIN_Y + (node_y * 128) + 48; -- } -- -- private List constructPath(AbstractNode nodeValue) -- { -- final LinkedList path = new LinkedList<>(); -- int previousDirectionX = Integer.MIN_VALUE; -- int previousDirectionY = Integer.MIN_VALUE; -- int directionX, directionY; -+ // create empty list -+ final LinkedList list = new LinkedList<>(); - -- AbstractNode node = nodeValue; -- while (node.getParent() != null) -+ // set direction X/Y -+ int dx = 0; -+ int dy = 0; -+ -+ // get target parent -+ Node target = node; -+ Node parent = target.getParent(); -+ -+ // while parent exists -+ int count = 0; -+ while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { -- directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); -- directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); -+ // get parent <> target direction X/Y -+ final int nx = parent.getLoc().getGeoX() - target.getLoc().getGeoX(); -+ final int ny = parent.getLoc().getGeoY() - target.getLoc().getGeoY(); - -- // only add a new route point if moving direction changes -- if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) -+ // direction has changed? -+ if ((dx != nx) || (dy != ny)) - { -- previousDirectionX = directionX; -- previousDirectionY = directionY; -+ // add node to the beginning of the list -+ list.addFirst(target.getLoc()); - -- path.addFirst(node.getLoc()); -- node.setLoc(null); -+ // update direction X/Y -+ dx = nx; -+ dy = ny; - } - -- node = node.getParent(); -+ // move to next node, set target and get its parent -+ target = parent; -+ parent = target.getParent(); - } - -- return path; -+ // return list -+ return list; - } - -- private CellNodeBuffer alloc(int size) -+ /** -+ * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer. -+ * @param size : pre-calculated minimal required size -+ * @return NodeBuffer : buffer -+ */ -+ private final NodeBuffer getBuffer(int size) - { -- CellNodeBuffer current = null; -- for (BufferInfo i : _buffers) -+ NodeBuffer current = null; -+ for (BufferHolder holder : _buffers) - { -- if (i.mapSize >= size) -+ // Find proper size of buffer -+ if (holder._size < size) - { -- for (CellNodeBuffer buf : i.buffer) -+ continue; -+ } -+ -+ // Find unlocked NodeBuffer -+ for (NodeBuffer buffer : holder._buffer) -+ { -+ if (!buffer.isLocked()) - { -- if (buf.lock()) -- { -- current = buf; -- break; -- } -+ continue; - } -- if (current != null) -- { -- break; -- } - -- // not found, allocate temporary buffer -- current = new CellNodeBuffer(i.mapSize); -- current.lock(); -- if (i.buffer.size() < i.count) -- { -- i.buffer.add(current); -- break; -- } -+ return buffer; - } -+ -+ // NodeBuffer not found, allocate temporary buffer -+ current = new NodeBuffer(holder._size); -+ current.isLocked(); - } - - return current; - } - -- private static final class BufferInfo -+ /** -+ * NodeBuffer container with specified size and count of separate buffers. -+ */ -+ private static final class BufferHolder - { -- final int mapSize; -- final int count; -- ArrayList buffer; -+ final int _size; -+ List _buffer; - -- public BufferInfo(int size, int cnt) -+ public BufferHolder(int size, int count) - { -- mapSize = size; -- count = cnt; -- buffer = new ArrayList<>(count); -+ _size = size; -+ _buffer = new ArrayList<>(count); -+ for (int i = 0; i < count; i++) -+ { -+ _buffer.add(new NodeBuffer(size)); -+ } - } - } -- -- public static GeoEnginePathfinding getInstance() -- { -- return SingletonHolder.INSTANCE; -- } -- -- private static class SingletonHolder -- { -- protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); -- } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (working copy) -@@ -0,0 +1,197 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @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 height of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getHeightNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first above given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, above given coordinates. -+ */ -+ public abstract short getHeightAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first below given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is closest to given coordinates. -+ * @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 the NSWE flag byte of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getNsweNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first above given coordinates. -+ * @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 getNsweAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first below given coordinates. -+ * @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 getNsweBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is closes layer to given coordinates. -+ * @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. -+ * @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 above given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is first layer below given coordinates. -+ * @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 index to data of the cell, which is first layer below given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexBelowOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeight(int index); -+ -+ /** -+ * Returns the height of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightOriginal(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNswe(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNsweOriginal(int index); -+ -+ /** -+ * Sets the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @param nswe : New NSWE flag byte. -+ */ -+ public abstract void setNswe(int index, byte nswe); -+ -+ /** -+ * Saves the block in L2D format to {@link BufferedOutputStream}. Used only for L2D geodata conversion. -+ * @param stream : The stream. -+ * @throws IOException : Can't save the block to steam. -+ */ -+ public abstract void saveBlock(BufferedOutputStream stream) throws IOException; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (working copy) -@@ -0,0 +1,252 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockComplex(ByteBuffer bb, GeoFormat format) -+ { -+ // initialize buffer -+ _buffer = new byte[GeoStructure.BLOCK_CELLS * 3]; -+ -+ // load data -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // 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); -+ } -+ else -+ { -+ // get nswe -+ final byte nswe = bb.get(); -+ _buffer[i * 3] = nswe; -+ -+ // get height -+ final short height = bb.getShort(); -+ _buffer[(i * 3) + 1] = (byte) (height & 0x00FF); -+ _buffer[(i * 3) + 2] = (byte) (height >> 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height > worldZ ? height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height < worldZ ? height : Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public 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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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 int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_COMPLEX_L2D); -+ -+ // write block data -+ stream.write(_buffer, 0, GeoStructure.BLOCK_CELLS * 3); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (working copy) -@@ -0,0 +1,176 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockFlat extends ABlock -+{ -+ protected final short _height; -+ protected byte _nswe; -+ -+ /** -+ * Creates FlatBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockFlat(ByteBuffer bb, GeoFormat format) -+ { -+ _height = bb.getShort(); -+ _nswe = format != GeoFormat.L2D ? 0x0F : (byte) (0xFF); -+ if (format == GeoFormat.L2OFF) -+ { -+ bb.getShort(); -+ } -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return true; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height > worldZ ? _height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height < worldZ ? _height : Short.MAX_VALUE; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height > worldZ ? _nswe : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height < worldZ ? _nswe : 0; -+ } -+ -+ @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return index -+ return _height < worldZ ? 0 : -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _nswe = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_FLAT_L2D); -+ -+ // write height -+ stream.write((byte) (_height & 0x00FF)); -+ stream.write((byte) (_height >> 8)); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (working copy) -@@ -0,0 +1,465 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+import java.nio.ByteOrder; -+import java.util.Arrays; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockMultilayer extends ABlock -+{ -+ private static final int MAX_LAYERS = Byte.MAX_VALUE; -+ -+ private static ByteBuffer _temp; -+ -+ /** -+ * Initializes the temporarily buffer. -+ */ -+ public static void initialize() -+ { -+ // initialize temporarily buffer and sorting mechanism -+ _temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3); -+ _temp.order(ByteOrder.LITTLE_ENDIAN); -+ } -+ -+ /** -+ * Releases temporarily buffer. -+ */ -+ public static void release() -+ { -+ _temp = null; -+ } -+ -+ protected byte[] _buffer; -+ -+ /** -+ * Implicit constructor for children class. -+ */ -+ protected BlockMultilayer() -+ { -+ _buffer = null; -+ } -+ -+ /** -+ * Creates MultilayerBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockMultilayer(ByteBuffer bb, GeoFormat format) -+ { -+ // 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 = format != GeoFormat.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++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // get data -+ final short data = bb.getShort(); -+ -+ // add nswe and height -+ _temp.put((byte) (data & 0x000F)); -+ _temp.putShort((short) ((short) (data & 0xFFF0) >> 1)); -+ } -+ else -+ { -+ // add nswe -+ _temp.put(bb.get()); -+ -+ // add height -+ _temp.putShort(bb.getShort()); -+ } -+ } -+ } -+ -+ // 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 height -+ if (height > worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, return minimum value -+ return Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 height -+ if (height < worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, return maximum value -+ return Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 nswe -+ if (height > worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 nswe -+ if (height < worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @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 first layer data (first from bottom) -+ byte layers = _buffer[index++]; -+ -+ // loop though all cell layers, find closest layer -+ 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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ // get height -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(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]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ // get nswe -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ // set nswe -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_MULTILAYER_L2D); -+ -+ // for each cell -+ int index = 0; -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ // write layers count -+ final byte layers = _buffer[index++]; -+ stream.write(layers); -+ -+ // write cell data -+ stream.write(_buffer, index, layers * 3); -+ -+ // move index to next cell -+ index += layers * 3; -+ } -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (working copy) -@@ -0,0 +1,150 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockNull extends ABlock -+{ -+ private final byte _nswe; -+ -+ public BlockNull() -+ { -+ _nswe = (byte) 0xFF; -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return false; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweBelow(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) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) -+ { -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (nonexistent) -@@ -1,48 +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 . -- */ --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() -- { -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (nonexistent) -@@ -1,77 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (nonexistent) -@@ -1,56 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (working copy) -@@ -0,0 +1,39 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public enum GeoFormat -+{ -+ L2J("%d_%d.l2j"), -+ L2OFF("%d_%d_conv.dat"), -+ L2D("%d_%d.l2d"); -+ -+ private final String _filename; -+ -+ private GeoFormat(String filename) -+ { -+ _filename = filename; -+ } -+ -+ public String getFilename() -+ { -+ return _filename; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (working copy) -@@ -0,0 +1,67 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.geoengine.GeoEngine; -+import org.l2jmobius.gameserver.model.Location; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoLocation extends Location -+{ -+ private byte _nswe; -+ -+ public GeoLocation(int x, int y, int z) -+ { -+ super(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public void set(int x, int y, short z) -+ { -+ super.setXYZ(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public int getGeoX() -+ { -+ return _x; -+ } -+ -+ public int getGeoY() -+ { -+ return _y; -+ } -+ -+ @Override -+ public int getX() -+ { -+ return GeoEngine.getWorldX(_x); -+ } -+ -+ @Override -+ public int getY() -+ { -+ return GeoEngine.getWorldY(_y); -+ } -+ -+ public byte getNSWE() -+ { -+ return _nswe; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (working copy) -@@ -0,0 +1,70 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoStructure -+{ -+ // cells -+ public static final byte CELL_FLAG_E = 1 << 0; -+ public static final byte CELL_FLAG_W = 1 << 1; -+ public static final byte CELL_FLAG_S = 1 << 2; -+ public static final byte CELL_FLAG_N = 1 << 3; -+ public static final byte CELL_FLAG_SE = 1 << 4; -+ public static final byte CELL_FLAG_SW = 1 << 5; -+ public static final byte CELL_FLAG_NE = 1 << 6; -+ public static final byte CELL_FLAG_NW = (byte) (1 << 7); -+ -+ public static final int CELL_HEIGHT = 8; -+ public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6; -+ -+ // blocks -+ public static final byte TYPE_FLAT_L2J_L2OFF = 0; -+ public static final byte TYPE_FLAT_L2D = (byte) 0xD0; -+ public static final byte TYPE_COMPLEX_L2J = 1; -+ public static final byte TYPE_COMPLEX_L2OFF = 0x40; -+ public static final byte TYPE_COMPLEX_L2D = (byte) 0xD1; -+ 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) -+ public static final byte TYPE_MULTILAYER_L2D = (byte) 0xD2; -+ -+ 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; -+ -+ // regions -+ 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; -+ -+ // global geodata -+ private static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); -+ private 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; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (nonexistent) -@@ -1,42 +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 . -- */ --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); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (working copy) -@@ -0,0 +1,53 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public interface IGeoObject -+{ -+ /** -+ * Returns geodata X coordinate of the {@link IGeoObject}. -+ * @return int : Geodata X coordinate. -+ */ -+ int getGeoX(); -+ -+ /** -+ * Returns geodata Y coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Y coordinate. -+ */ -+ int getGeoY(); -+ -+ /** -+ * Returns geodata Z coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Z coordinate. -+ */ -+ int getGeoZ(); -+ -+ /** -+ * Returns height of the {@link IGeoObject}. -+ * @return int : Height. -+ */ -+ int getHeight(); -+ -+ /** -+ * Returns {@link IGeoObject} data. -+ * @return byte[][] : {@link IGeoObject} data. -+ */ -+ byte[][] getObjectGeoData(); -+} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (nonexistent) -@@ -1,47 +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 . -- */ --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(); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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); -- -- // 1 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (nonexistent) -@@ -1,55 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --/** -- * @author HorridoJoho -- */ --public final class NullRegion implements IRegion --{ -- public static final NullRegion INSTANCE = new NullRegion(); -- -- @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 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Region.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (nonexistent) -@@ -1,92 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (nonexistent) -@@ -1,87 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public abstract class AbstractNode --{ -- private T _loc; -- private AbstractNode _parent; -- -- public AbstractNode(T loc) -- { -- _loc = loc; -- } -- -- public void setParent(AbstractNode p) -- { -- _parent = p; -- } -- -- public AbstractNode getParent() -- { -- return _parent; -- } -- -- public T getLoc() -- { -- return _loc; -- } -- -- public void setLoc(T l) -- { -- _loc = l; -- } -- -- @Override -- public int hashCode() -- { -- final int prime = 31; -- int result = 1; -- result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); -- return result; -- } -- -- @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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (nonexistent) -@@ -1,33 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --/** -- * @author -Nemesiss- -- */ --public abstract class AbstractNodeLoc --{ -- public abstract int getX(); -- -- public abstract int getY(); -- -- public abstract int getZ(); -- -- public abstract int getNodeX(); -- -- public abstract int getNodeY(); --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (nonexistent) -@@ -1,67 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public class CellNode extends AbstractNode --{ -- 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (nonexistent) -@@ -1,348 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --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 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) -- { -- _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(); -- } -- -- public final List debugPath() -- { -- final List 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 void getNeighbors() -- { -- if (!_current.getLoc().canGoAll()) -- { -- 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); -- } -- -- // SouthEast -- if ((nodeE != null) && (nodeS != null)) -- { -- if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) -- { -- addNode(x + 1, y + 1, z, true); -- } -- } -- -- // SouthWest -- if ((nodeS != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) -- { -- addNode(x - 1, y + 1, z, true); -- } -- } -- -- // NorthEast -- if ((nodeN != null) && (nodeE != null)) -- { -- if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) -- { -- addNode(x + 1, y - 1, z, true); -- } -- } -- -- // NorthWest -- if ((nodeN != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) -- { -- addNode(x - 1, y - 1, z, true); -- } -- } -- } -- -- private 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(); -- // reinit node if needed -- if (result.getLoc() != null) -- { -- result.getLoc().set(x, y, z); -- } -- else -- { -- result.setLoc(new NodeLoc(x, y, z)); -- } -- } -- -- return result; -- } -- -- private 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 boolean isHighWeight(int x, int y, int z) -- { -- final CellNode result = getNode(x, y, z); -- if (result == null) -- { -- return true; -- } -- -- if (!result.getLoc().canGoAll()) -- { -- return true; -- } -- if (Math.abs(result.getLoc().getZ() - z) > 16) -- { -- return true; -- } -- -- return false; -- } -- -- private 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (working copy) -@@ -0,0 +1,87 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+ -+/** -+ * @author Hasha -+ */ -+public class Node -+{ -+ // node coords and nswe flag -+ private GeoLocation _loc; -+ -+ // node parent (for reverse path construction) -+ private Node _parent; -+ // node child (for moving over nodes during iteration) -+ private Node _child; -+ -+ // node G cost (movement cost = parent movement cost + current movement cost) -+ private double _cost = -1000; -+ -+ public void setLoc(int x, int y, int z) -+ { -+ _loc = new GeoLocation(x, y, z); -+ } -+ -+ public GeoLocation getLoc() -+ { -+ return _loc; -+ } -+ -+ public void setParent(Node parent) -+ { -+ _parent = parent; -+ } -+ -+ public Node getParent() -+ { -+ return _parent; -+ } -+ -+ public void setChild(Node child) -+ { -+ _child = child; -+ } -+ -+ public Node getChild() -+ { -+ return _child; -+ } -+ -+ public void setCost(double cost) -+ { -+ _cost = cost; -+ } -+ -+ public double getCost() -+ { -+ return _cost; -+ } -+ -+ public void free() -+ { -+ // reset node location -+ _loc = null; -+ -+ // reset node parent, child and cost -+ _parent = null; -+ _child = null; -+ _cost = -1000; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (working copy) -@@ -0,0 +1,306 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import java.util.concurrent.locks.ReentrantLock; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+ -+/** -+ * @author DS, Hasha; Credits to Diamond -+ */ -+public class NodeBuffer -+{ -+ private final ReentrantLock _lock = new ReentrantLock(); -+ private final int _size; -+ private final Node[][] _buffer; -+ -+ // center coordinates -+ private int _cx = 0; -+ private int _cy = 0; -+ -+ // target coordinates -+ private int _gtx = 0; -+ private int _gty = 0; -+ private short _gtz = 0; -+ -+ private Node _current = null; -+ -+ /** -+ * Constructor of NodeBuffer. -+ * @param size : one dimension size of buffer -+ */ -+ public NodeBuffer(int size) -+ { -+ // set size -+ _size = size; -+ -+ // initialize buffer -+ _buffer = new Node[size][size]; -+ for (int x = 0; x < size; x++) -+ { -+ for (int y = 0; y < size; y++) -+ { -+ _buffer[x][y] = 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 Node : first node of path -+ */ -+ public Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) -+ { -+ // set coordinates (middle of the line (gox,goy) - (gtx,gty), will be in the center of the buffer) -+ _cx = gox + ((gtx - gox - _size) / 2); -+ _cy = goy + ((gty - goy - _size) / 2); -+ -+ _gtx = gtx; -+ _gty = gty; -+ _gtz = gtz; -+ -+ _current = getNode(gox, goy, goz); -+ _current.setCost(getCostH(gox, goy, goz)); -+ -+ int count = 0; -+ do -+ { -+ // reached target? -+ if ((_current.getLoc().getGeoX() == _gtx) && (_current.getLoc().getGeoY() == _gty) && (Math.abs(_current.getLoc().getZ() - _gtz) < 8)) -+ { -+ return _current; -+ } -+ -+ // expand current node -+ expand(); -+ -+ // move pointer -+ _current = _current.getChild(); -+ } -+ while ((_current != null) && (count++ < Config.MAX_ITERATIONS)); -+ -+ return null; -+ } -+ -+ public boolean isLocked() -+ { -+ return _lock.tryLock(); -+ } -+ -+ public void free() -+ { -+ _current = null; -+ -+ for (Node[] nodes : _buffer) -+ { -+ for (Node node : nodes) -+ { -+ if (node.getLoc() != null) -+ { -+ node.free(); -+ } -+ } -+ } -+ -+ _lock.unlock(); -+ } -+ -+ /** -+ * Check _current Node and add its neighbors to the buffer. -+ */ -+ private final void expand() -+ { -+ // can't move anywhere, don't expand -+ final byte nswe = _current.getLoc().getNSWE(); -+ if (nswe == 0) -+ { -+ return; -+ } -+ -+ // get geo coords of the node to be expanded -+ final int x = _current.getLoc().getGeoX(); -+ final int y = _current.getLoc().getGeoY(); -+ final short z = (short) _current.getLoc().getZ(); -+ -+ // can move north, expand -+ if ((nswe & GeoStructure.CELL_FLAG_N) != 0) -+ { -+ addNode(x, y - 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move south, expand -+ if ((nswe & GeoStructure.CELL_FLAG_S) != 0) -+ { -+ addNode(x, y + 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_W) != 0) -+ { -+ addNode(x - 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_E) != 0) -+ { -+ addNode(x + 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move north-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NW) != 0) -+ { -+ addNode(x - 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move north-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NE) != 0) -+ { -+ addNode(x + 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SW) != 0) -+ { -+ addNode(x - 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SE) != 0) -+ { -+ addNode(x + 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ } -+ -+ /** -+ * Returns node, if it exists in buffer. -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param z : node Z coord -+ * @return Node : node, if exits in buffer -+ */ -+ private final Node getNode(int x, int y, short z) -+ { -+ // check node X out of coordinates -+ final int ix = x - _cx; -+ if ((ix < 0) || (ix >= _size)) -+ { -+ return null; -+ } -+ -+ // check node Y out of coordinates -+ final int iy = y - _cy; -+ if ((iy < 0) || (iy >= _size)) -+ { -+ return null; -+ } -+ -+ // get node -+ final Node result = _buffer[ix][iy]; -+ -+ // check and update -+ if (result.getLoc() == null) -+ { -+ result.setLoc(x, y, z); -+ } -+ -+ // return node -+ return result; -+ } -+ -+ /** -+ * Add node given by coordinates to the buffer. -+ * @param x : geo X coord -+ * @param y : geo Y coord -+ * @param z : geo Z coord -+ * @param weight : weight of movement to new node -+ */ -+ private final void addNode(int x, int y, short z, int weight) -+ { -+ // get node to be expanded -+ final Node node = getNode(x, y, z); -+ if (node == null) -+ { -+ return; -+ } -+ -+ // Z distance between nearby cells is higher than cell size -+ if (node.getLoc().getZ() > (z + (2 * GeoStructure.CELL_HEIGHT))) -+ { -+ return; -+ } -+ -+ // node was already expanded, return -+ if (node.getCost() >= 0) -+ { -+ return; -+ } -+ -+ node.setParent(_current); -+ if (node.getLoc().getNSWE() != (byte) 0xFF) -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + (weight * Config.OBSTACLE_MULTIPLIER)); -+ } -+ else -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + weight); -+ } -+ -+ Node current = _current; -+ int count = 0; -+ while ((current.getChild() != null) && (count < (Config.MAX_ITERATIONS * 4))) -+ { -+ count++; -+ if (current.getChild().getCost() > node.getCost()) -+ { -+ node.setChild(current.getChild()); -+ break; -+ } -+ current = current.getChild(); -+ } -+ -+ if (count >= (Config.MAX_ITERATIONS * 4)) -+ { -+ System.err.println("Pathfinding: too long loop detected, cost:" + node.getCost()); -+ } -+ -+ current.setChild(node); -+ } -+ -+ /** -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param i : node Z coord -+ * @return double : node cost -+ */ -+ private final double getCostH(int x, int y, int i) -+ { -+ final int dX = x - _gtx; -+ final int dY = y - _gty; -+ final int dZ = (i - _gtz) / GeoStructure.CELL_HEIGHT; -+ -+ // return (Math.abs(dX) + Math.abs(dY) + Math.abs(dZ)) * Config.HEURISTIC_WEIGHT; // Manhattan distance -+ return Math.sqrt((dX * dX) + (dY * dY) + (dZ * dZ)) * Config.HEURISTIC_WEIGHT; // Direct distance -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -- --/** -- * @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 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 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; -- // return super.hashCode(); -- } -- -- @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; -- if (_x != other._x) -- { -- return false; -- } -- if (_y != other._y) -- { -- return false; -- } -- if (_goNorth != other._goNorth) -- { -- return false; -- } -- if (_goEast != other._goEast) -- { -- return false; -- } -- if (_goSouth != other._goSouth) -- { -- return false; -- } -- if (_goWest != other._goWest) -- { -- return false; -- } -- if (_geoHeight != other._geoHeight) -- { -- return false; -- } -- return true; -- } --} -Index: java/org/l2jmobius/gameserver/model/actor/Creature.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/Creature.java (revision 8428) -+++ java/org/l2jmobius/gameserver/model/actor/Creature.java (working copy) -@@ -65,8 +65,6 @@ - import org.l2jmobius.gameserver.enums.TeleportWhereType; - import org.l2jmobius.gameserver.enums.UserInfoType; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; - import org.l2jmobius.gameserver.instancemanager.IdManager; - import org.l2jmobius.gameserver.instancemanager.MapRegionManager; - import org.l2jmobius.gameserver.instancemanager.QuestManager; -@@ -2566,7 +2564,7 @@ - - public boolean disregardingGeodata; - public int onGeodataPathIndex; -- public List geoPath; -+ public List geoPath; - public int geoPathAccurateTx; - public int geoPathAccurateTy; - public int geoPathGtx; -@@ -3443,7 +3441,7 @@ - if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) - { - // Path calculation -- overrides previous movement check -- m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); -+ m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); - if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found - { - if (isPlayer() && !_isFlying && !isInWater) -Index: java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (revision 8424) -+++ java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (working copy) -@@ -12746,7 +12746,7 @@ - { - if (Config.CORRECT_PLAYER_Z) - { -- final int nearestZ = GeoEngine.getInstance().getHigherHeight(getX(), getY(), getZ()); -+ final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); - if (getZ() < nearestZ) - { - teleToLocation(new Location(getX(), getY(), nearestZ)); -Index: java/org/l2jmobius/gameserver/util/GeoUtils.java -=================================================================== ---- java/org/l2jmobius/gameserver/util/GeoUtils.java (revision 8409) -+++ java/org/l2jmobius/gameserver/util/GeoUtils.java (working copy) -@@ -19,7 +19,7 @@ - import java.awt.Color; - - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; - import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; - -@@ -26,25 +26,25 @@ - /** - * @author HorridoJoho - */ --public final class GeoUtils -+public class GeoUtils - { - public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); - - final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); - - while (iter.next()) - { -- final int wx = GeoEngine.getInstance().getWorldX(iter.x()); -- final int wy = GeoEngine.getInstance().getWorldY(iter.y()); -+ final int wx = GeoEngine.getWorldX(iter.x()); -+ final int wy = GeoEngine.getWorldY(iter.y()); - - prim.addPoint(Color.RED, wx, wy, z); - } -@@ -53,21 +53,21 @@ - - public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); -- int wy = GeoEngine.getInstance().getWorldY(prevY); -+ int wx = GeoEngine.getWorldX(prevX); -+ int wy = GeoEngine.getWorldY(prevY); - int wz = iter.z(); - prim.addPoint(Color.RED, wx, wy, wz); - -@@ -78,8 +78,8 @@ - - if ((curX != prevX) || (curY != prevY)) - { -- wx = GeoEngine.getInstance().getWorldX(curX); -- wy = GeoEngine.getInstance().getWorldY(curY); -+ wx = GeoEngine.getWorldX(curX); -+ wy = GeoEngine.getWorldY(curY); - wz = iter.z(); - - prim.addPoint(Color.RED, wx, wy, wz); -@@ -93,7 +93,7 @@ - - private static Color getDirectionColor(int x, int y, int z, int nswe) - { -- if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) -+ if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) - { - return Color.GREEN; - } -@@ -109,9 +109,8 @@ - int iPacket = 0; - - ExServerPrimitive exsp = null; -- final GeoEngine ge = GeoEngine.getInstance(); -- final int playerGx = ge.getGeoX(player.getX()); -- final int playerGy = ge.getGeoY(player.getY()); -+ final int playerGx = GeoEngine.getGeoX(player.getX()); -+ final int playerGy = GeoEngine.getGeoY(player.getY()); - for (int dx = -geoRadius; dx <= geoRadius; ++dx) - { - for (int dy = -geoRadius; dy <= geoRadius; ++dy) -@@ -135,12 +134,12 @@ - final int gx = playerGx + dx; - final int gy = playerGy + dy; - -- final int x = ge.getWorldX(gx); -- final int y = ge.getWorldY(gy); -- final int z = ge.getNearestZ(gx, gy, player.getZ()); -+ final int x = GeoEngine.getWorldX(gx); -+ final int y = GeoEngine.getWorldY(gy); -+ final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); - - // north arrow -- Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); -+ Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); - 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); -@@ -147,7 +146,7 @@ - exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z); - - // east arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); - 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); -@@ -154,13 +153,13 @@ - exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z); - - // south arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); - 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, Cell.NSWE_WEST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); - 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); -@@ -188,15 +187,15 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_EAST; -+ return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_EAST; -+ return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; - } - else - { -- return Cell.NSWE_EAST; -+ return GeoStructure.CELL_FLAG_E; // Direction.EAST; - } - } - else if (x < lastX) // west -@@ -203,26 +202,27 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_WEST; -+ return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_WEST; -+ return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; - } - else - { -- return Cell.NSWE_WEST; -+ return GeoStructure.CELL_FLAG_W; // Direction.WEST; - } - } -- else // unchanged x -+ else -+ // unchanged x - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH; -+ return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH; -+ return GeoStructure.CELL_FLAG_N; // Direction.NORTH; - } - else - { -Index: java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java -=================================================================== ---- java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (nonexistent) -+++ java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (working copy) -@@ -0,0 +1,386 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.tools.geodataconverter; -+ -+import java.io.BufferedOutputStream; -+import java.io.File; -+import java.io.FileOutputStream; -+import java.io.RandomAccessFile; -+import java.nio.ByteOrder; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.Scanner; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.commons.util.PropertiesParser; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoDataConverter -+{ -+ private static GeoFormat _format; -+ private static ABlock[][] _blocks; -+ -+ public static void main(String[] args) -+ { -+ // initialize config -+ loadGeoengineConfigs(); -+ -+ // get geodata format -+ String type = ""; -+ try (Scanner scn = new Scanner(System.in)) -+ { -+ while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) -+ { -+ System.out.println("GeoDataConverter: Select source geodata type:"); -+ System.out.println(" J: L2J (e.g. 23_22.l2j)"); -+ System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); -+ System.out.println(" E: Exit"); -+ System.out.print("Choice: "); -+ type = scn.next(); -+ } -+ } -+ if (type.equalsIgnoreCase("E")) -+ { -+ System.exit(0); -+ } -+ -+ _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; -+ -+ // start conversion -+ System.out.println("GeoDataConverter: Converting all " + _format + " files."); -+ -+ // initialize geodata container -+ _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files -+ int converted = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) -+ { -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) -+ { -+ final String input = String.format(_format.getFilename(), rx, ry); -+ final String filepath = Config.GEODATA_PATH; -+ final File f = new File(filepath + input); -+ if (f.exists() && !f.isDirectory()) -+ { -+ // load geodata -+ if (!loadGeoBlocks(input)) -+ { -+ System.out.println("GeoDataConverter: Unable to load " + input + " region file."); -+ continue; -+ } -+ -+ // recalculate nswe -+ if (!recalculateNswe()) -+ { -+ System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); -+ continue; -+ } -+ -+ // save geodata -+ final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); -+ if (!saveGeoBlocks(output)) -+ { -+ System.out.println("GeoDataConverter: Unable to save " + output + " region file."); -+ continue; -+ } -+ -+ converted++; -+ System.out.println("GeoDataConverter: Created " + output + " region file."); -+ } -+ } -+ } -+ System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); -+ } -+ -+ /** -+ * Loads geo blocks from buffer of the region file. -+ * @param filename : The name of the to load. -+ * @return boolean : True when successful. -+ */ -+ private static boolean loadGeoBlocks(String filename) -+ { -+ // region file is load-able, try to load it -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) -+ { -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) -+ if (_format == GeoFormat.L2OFF) -+ { -+ for (int i = 0; i < 18; i++) -+ { -+ buffer.get(); -+ } -+ } -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ if (_format == GeoFormat.L2J) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2J: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2J: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ else -+ { -+ // get block type -+ final short type = buffer.getShort(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ default: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ if (buffer.remaining() > 0) -+ { -+ System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ return false; -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); -+ return false; -+ } -+ } -+ -+ /** -+ * Recalculate diagonal flags for the region file. -+ * @return boolean : True when successful. -+ */ -+ private static boolean recalculateNswe() -+ { -+ try -+ { -+ for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) -+ { -+ for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) -+ { -+ // get block -+ final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // skip flat blocks -+ if (block instanceof BlockFlat) -+ { -+ continue; -+ } -+ -+ // for complex and multilayer blocks go though all layers -+ short height = Short.MAX_VALUE; -+ int index; -+ while ((index = block.getIndexBelow(x, y, height)) != -1) -+ { -+ // get height and nswe -+ height = block.getHeight(index); -+ byte nswe = block.getNswe(index); -+ -+ // update nswe with diagonal flags -+ nswe = updateNsweBelow(x, y, height, nswe); -+ -+ // set nswe of the cell -+ block.setNswe(index, nswe); -+ } -+ } -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ /** -+ * Updates the NSWE flag with diagonal flags. -+ * @param x : Geodata X coordinate. -+ * @param y : Geodata Y coordinate. -+ * @param z : Geodata Z coordinate. -+ * @param nsweValue : NSWE flag to be updated. -+ * @return byte : Updated NSWE flag. -+ */ -+ private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) -+ { -+ byte nswe = nsweValue; -+ -+ // calculate virtual layer height -+ final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); -+ -+ // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) -+ final byte nsweN = getNsweBelow(x, y - 1, height); -+ final byte nsweS = getNsweBelow(x, y + 1, height); -+ final byte nsweW = getNsweBelow(x - 1, y, height); -+ final byte nsweE = getNsweBelow(x + 1, y, height); -+ -+ // north-west -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NW; -+ } -+ -+ // north-east -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NE; -+ } -+ -+ // south-west -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SW; -+ } -+ -+ // south-east -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SE; -+ } -+ -+ return nswe; -+ } -+ -+ private static byte getNsweBelow(int geoX, int geoY, short worldZ) -+ { -+ // out of geo coordinates -+ if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) -+ { -+ return 0; -+ } -+ -+ // out of geo coordinates -+ if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) -+ { -+ return 0; -+ } -+ -+ // get block -+ final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // get index, when valid, return nswe -+ final int index = block.getIndexBelow(geoX, geoY, worldZ); -+ return index == -1 ? 0 : block.getNswe(index); -+ } -+ -+ /** -+ * Save region file to file. -+ * @param filename : The name of file to save. -+ * @return boolean : True when successful. -+ */ -+ private static boolean saveGeoBlocks(String filename) -+ { -+ try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) -+ { -+ // loop over region blocks and save each block -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[ix][iy].saveBlock(bos); -+ } -+ } -+ -+ // flush data to file -+ bos.flush(); -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ private static void loadGeoengineConfigs() -+ { -+ final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); -+ Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); -+ Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); -+ Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); -+ Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); -+ Config.PATHFINDING = geoData.getBoolean("PathFinding", true); -+ Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -+ Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); -+ Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); -+ Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); -+ Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); -+ Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); -+ } -+} -\ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/geodata/Readme.txt b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/geodata/Readme.txt index a480c8c380..2611882e56 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/geodata/Readme.txt +++ b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/geodata/Readme.txt @@ -23,8 +23,8 @@ a - Prerequisites b - Make it work ---------------------------------------------- -To make geodata working: -* unpack your geodata files into "/data/geodata" folder -* open "/config/GeoEngine.ini" with your favorite text editor and then edit following config: - - CoordSynchronize = 2 -* If you do not use any geodata files, the server will automatically change this setting to -1. +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. diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/ai/others/FleeMonsters.java b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/ai/others/FleeMonsters.java index 4562336103..a971742033 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/ai/others/FleeMonsters.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/ai/others/FleeMonsters.java @@ -59,7 +59,7 @@ public class FleeMonsters extends AbstractNpcAI final int posX = (int) (npc.getX() + (FLEE_DISTANCE * Math.cos(radians))); final int posY = (int) (npc.getY() + (FLEE_DISTANCE * Math.sin(radians))); final int posZ = npc.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, attacker.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); return super.onAttack(npc, attacker, damage, isSummon); } diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/ai/others/RandomWalkingGuards.java b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/ai/others/RandomWalkingGuards.java index b99becfdc6..780502d5d6 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/ai/others/RandomWalkingGuards.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/ai/others/RandomWalkingGuards.java @@ -55,7 +55,7 @@ public class RandomWalkingGuards extends AbstractNpcAI if (!npc.isInCombat()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, Config.MAX_DRIFT_RANGE); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("RANDOM_WALK", getRandom(MIN_WALK_DELAY, MAX_WALK_DELAY), npc, null); } diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index 524652cbc1..18511c6541 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { @@ -133,8 +133,8 @@ public class AdminGeodata implements IAdminCommandHandler } case "admin_geomap": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y + " (" + ((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + "," + ((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + " to " + ((((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + "," + ((((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + ")"); break; } diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java index f3dffa1227..76572f7f25 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java @@ -57,8 +57,8 @@ public class AdminMissingHtmls implements IAdminCommandHandler { case "admin_geomap_missing_htmls": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final int topLeftX = (x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE; final int topLeftY = (y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE; final int bottomRightX = (((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1; diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index fdb2319138..a0a34f1051 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -19,9 +19,9 @@ package handlers.admincommandhandlers; import java.util.List; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; +import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.util.BuilderUtil; @@ -44,13 +44,13 @@ public class AdminPathNode implements IAdminCommandHandler } if (activeChar.getTarget() != null) { - final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { BuilderUtil.sendSysMessage(activeChar, "No Route!"); return true; } - for (AbstractNodeLoc a : path) + for (Location a : path) { BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/Blink.java b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/Blink.java index 22f2c84eae..22697e8e20 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/Blink.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/Blink.java @@ -88,7 +88,7 @@ public class Blink extends AbstractEffect final int y = effected.getY() + y1; final int z = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, destination, _flyType, _flySpeed, _flyDelay, _animationSpeed)); diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/Fear.java b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/Fear.java index 95a5afdf98..88c795e04b 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/Fear.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/Fear.java @@ -100,7 +100,7 @@ public class Fear extends AbstractEffect final int posY = (int) (effected.getY() + (FEAR_RANGE * Math.sin(radians))); final int posZ = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); } } diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java index fddd94c60a..55c2e78d8a 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java @@ -57,7 +57,7 @@ public class FlyAway extends AbstractEffect final int y = (int) (effector.getY() - (nRadius * (dy / distance))); final int z = effector.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.broadcastPacket(new FlyToLocation(effected, destination, FlyType.THROW_UP)); effected.setXYZ(destination); diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java index 1fbd8239b0..89ad8670dc 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java @@ -133,7 +133,7 @@ public class KnockBack extends AbstractEffect final int x = (int) (effected.getX() + (_distance * Math.cos(radians))); final int y = (int) (effected.getY() + (_distance * Math.sin(radians))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, loc, _type, _speed, _delay, _animationSpeed)); diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/PullBack.java b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/PullBack.java index ed6a37eff6..38fe2fe9d0 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/PullBack.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/PullBack.java @@ -73,7 +73,7 @@ public class PullBack extends AbstractEffect } // In retail, you get debuff, but you are not even moved if there is obstacle. You are still disabled from using skills and moving though. - if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effector.getInstanceWorld())) + if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effected.getInstanceWorld())) { effected.broadcastPacket(new FlyToLocation(effected, effector, _type, _speed, _delay, _animationSpeed)); effected.setXYZ(effector.getX(), effector.getY(), GeoEngine.getInstance().getHeight(effector.getX(), effector.getY(), effector.getZ()) + 10); diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java index d6f9664141..813e496c47 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java @@ -87,7 +87,7 @@ public class TeleportToSummon extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = summon.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java index d3c263978c..c9dc34ac22 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java @@ -76,7 +76,7 @@ public class TeleportToTarget extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java index f2148adb54..c7ca6f64cb 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java @@ -65,7 +65,7 @@ public class RollingDice implements IItemHandler final int x = player.getX() + x1; final int y = player.getY() + y1; final int z = player.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); Broadcast.toSelfAndKnownPlayers(player, new Dice(player.getObjectId(), itemId, number, destination.getX(), destination.getY(), destination.getZ())); final SystemMessage sm = new SystemMessage(SystemMessageId.C1_HAS_ROLLED_A_S2); diff --git a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/targethandlers/Ground.java b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/targethandlers/Ground.java index 82ab321c29..d8c73627f5 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/targethandlers/Ground.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/dist/game/data/scripts/handlers/targethandlers/Ground.java @@ -52,7 +52,7 @@ public class Ground implements ITargetTypeHandler return null; } - if (!GeoEngine.getInstance().canSeeTarget(creature, worldPosition)) + if (!GeoEngine.getInstance().canSeeLocation(creature, worldPosition)) { if (sendMessage) { diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/Config.java index 7ef488bf94..81835ba175 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/Config.java @@ -61,6 +61,7 @@ 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; @@ -906,12 +907,17 @@ public class Config // GeoEngine // -------------------------------------------------- public static Path GEODATA_PATH; + public static GeoType GEODATA_TYPE; public static boolean PATHFINDING; public static String PATHFIND_BUFFERS; - public static float LOW_WEIGHT; - public static float MEDIUM_WEIGHT; - public static float HIGH_WEIGHT; - public static float DIAGONAL_WEIGHT; + public static int MOVE_WEIGHT; + public static int MOVE_WEIGHT_DIAG; + public static int OBSTACLE_WEIGHT; + 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; /** Attribute System */ public static int S_WEAPON_STONE; @@ -2419,12 +2425,17 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); + GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); - MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); - HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); - DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); + MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); + OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); + HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); + MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); + MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); // Load AllowedPlayerRaces config file (if exists) final PropertiesParser AllowedPlayerRaces = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE); diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/commons/util/CommonUtil.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/commons/util/CommonUtil.java index 29a88cf20c..86fa98ff10 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/commons/util/CommonUtil.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/commons/util/CommonUtil.java @@ -591,6 +591,17 @@ public class CommonUtil return formatter.format(value); } + /** + * @param numToTest : The number to test. + * @param min : The minimum limit. + * @param max : The maximum limit. + * @return the number or one of the limit (mininum / maximum). + */ + public static int limit(int numToTest, int min, int max) + { + return (numToTest > max) ? max : ((numToTest < min) ? min : numToTest); + } + public static boolean isNullOrEmpty(CharSequence value) { return isNull(value) || (value.length() == 0); diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/commons/util/Point2D.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/commons/util/Point2D.java new file mode 100644 index 0000000000..ebb6272e5e --- /dev/null +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/commons/util/Point2D.java @@ -0,0 +1,180 @@ +/* + * 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 . + */ +package org.l2jmobius.commons.util; + +import java.util.Objects; + +/** + * A datatype used to retain a 2D (x/y) point. It got the capability to be set and cleaned. + */ +public class Point2D +{ + protected volatile int _x; + protected volatile int _y; + + public Point2D(int x, int y) + { + _x = x; + _y = y; + } + + @Override + public Point2D clone() + { + return new Point2D(_x, _y); + } + + @Override + public String toString() + { + return _x + ", " + _y; + } + + @Override + public int hashCode() + { + return Objects.hash(_x, _y); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final Point2D other = (Point2D) obj; + return (_x == other._x) && (_y == other._y); + } + + /** + * @param x : The X coord to test. + * @param y : The Y coord to test. + * @return True if all coordinates equals this {@link Point2D} coordinates. + */ + public boolean equals(int x, int y) + { + return (_x == x) && (_y == y); + } + + public int getX() + { + return _x; + } + + public void setX(int x) + { + _x = x; + } + + public int getY() + { + return _y; + } + + public void setY(int y) + { + _y = y; + } + + public void set(int x, int y) + { + _x = x; + _y = y; + } + + /** + * Refresh the current {@link Point2D} using a reference {@link Point2D} and a distance. The new destination is calculated to go in opposite side of the {@link Point2D} reference.
+ *
+ * This method is perfect to calculate fleeing characters position. + * @param referenceLoc : The Point2D used as position. + * @param distance : The distance to be set between current and new position. + */ + public void setFleeing(Point2D referenceLoc, int distance) + { + final double xDiff = referenceLoc.getX() - _x; + final double yDiff = referenceLoc.getY() - _y; + + final double yxRation = Math.abs(xDiff / yDiff); + + final int y = (int) (distance / (yxRation + 1)); + final int x = (int) (y * yxRation); + + _x += (xDiff < 0 ? x : -x); + _y += (yDiff < 0 ? y : -y); + } + + public void clean() + { + _x = 0; + _y = 0; + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @return The distance between this {@Point2D} and some given coordinates. + */ + public double distance2D(int x, int y) + { + final double dx = (double) _x - x; + final double dy = (double) _y - y; + + return Math.sqrt((dx * dx) + (dy * dy)); + } + + /** + * @param point : The {@link Point2D} to test. + * @return The distance between this {@Point2D} and the {@link Point2D} set as parameter. + */ + public double distance2D(Point2D point) + { + return distance2D(point.getX(), point.getY()); + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of some given coordinates. + */ + public boolean isIn2DRadius(int x, int y, int radius) + { + return distance2D(x, y) < radius; + } + + /** + * @param point : The Point2D to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of the {@link Point2D} set as parameter. + */ + public boolean isIn2DRadius(Point2D point, int radius) + { + return distance2D(point) < radius; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/AttackableAI.java index 547963e3e8..dc29d4cf6e 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/AttackableAI.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/ai/AttackableAI.java @@ -578,7 +578,7 @@ public class AttackableAI extends CreatureAI } // Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast) - final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); + final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); moveTo(moveLoc.getX(), moveLoc.getY(), moveLoc.getZ()); } } @@ -801,7 +801,7 @@ public class AttackableAI extends CreatureAI final int newZ = npc.getZ() + 30; // Mobius: Verify destination. Prevents wall collision issues and fixes monsters not avoiding obstacles. - moveTo(GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); + moveTo(GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); } return; } diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/data/SpawnTable.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/data/SpawnTable.java index a81201f69e..19f0db5b3e 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/data/SpawnTable.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/data/SpawnTable.java @@ -114,8 +114,8 @@ public class SpawnTable } // XML file for spawn - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); // Write info to XML @@ -192,8 +192,8 @@ public class SpawnTable if (update) { - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = spawn.getNpcSpawnTemplate() != null ? spawn.getNpcSpawnTemplate().getSpawnTemplate().getFile() : new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); final File tempFile = new File(spawnFile.getAbsolutePath().substring(Config.DATAPACK_ROOT.getAbsolutePath().length() + 1).replace('\\', '/') + ".tmp"); try diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/enums/GeoType.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/enums/GeoType.java new file mode 100644 index 0000000000..f77aa5eac4 --- /dev/null +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/enums/GeoType.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.enums; + +public enum GeoType +{ + L2J("%d_%d.l2j"), + L2OFF("%d_%d_conv.dat"); + + private final String _filename; + + private GeoType(String filename) + { + _filename = filename; + } + + public String getFilename() + { + return _filename; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java new file mode 100644 index 0000000000..e62f50a8df --- /dev/null +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java @@ -0,0 +1,144 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 9d255a5f54..7bef9c770b 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,72 +16,63 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.channels.FileChannel.MapMode; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.logging.Level; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; import java.util.logging.Logger; import org.l2jmobius.Config; +import org.l2jmobius.commons.util.CommonUtil; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; -import org.l2jmobius.gameserver.geoengine.geodata.IRegion; -import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; -import org.l2jmobius.gameserver.geoengine.geodata.Region; +import org.l2jmobius.gameserver.enums.GeoType; +import org.l2jmobius.gameserver.enums.MoveDirectionType; +import org.l2jmobius.gameserver.geoengine.geodata.ABlock; +import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; +import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; +import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; +import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.model.interfaces.ILocational; -import org.l2jmobius.gameserver.util.GeoUtils; -import org.l2jmobius.gameserver.util.LinePointIterator; -import org.l2jmobius.gameserver.util.LinePointIterator3D; -/** - * @author -Nemesiss-, HorridoJoho - */ public class GeoEngine { - private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - - private static final int WORLD_MIN_X = -655360; - private static final int WORLD_MIN_Y = -589824; - private static final int WORLD_MIN_Z = -16384; - - /** 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; - - /** The regions array */ - private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; - private static final int ELEVATED_SEE_OVER_DISTANCE = 2; - private static final int MAX_SEE_OVER_HEIGHT = 48; - protected GeoEngine() + private final ABlock[][] _blocks; + private final BlockNull _nullBlock; + + // Pre-allocated buffers. + private final BufferHolder[] _buffers; + + public GeoEngine() { LOGGER.info("GeoEngine: Initializing..."); - for (int i = 0; i < _regions.length(); i++) - { - _regions.set(i, NullRegion.INSTANCE); - } + // Initialize block container. + _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; + + // Load null block. + _nullBlock = new BlockNull(); + + // Initialize multilayer temporarily buffer. + BlockMultilayer.initialize(); + + // Load geo files according to geoengine config setup. int loaded = 0; try { @@ -92,23 +83,52 @@ public class GeoEngine final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); if (Files.exists(geoFilePath)) { - try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + // Region file is load-able, try to load it. + if (loadGeoBlocks(regionX, regionY)) { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); loaded++; } } + else + { + // Region file is not load-able, load null blocks. + loadNullBlocks(regionX, regionY); + } } } } catch (Exception e) { - LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + LOGGER.warning("GeoEngine: Failed to load geodata! " + e); System.exit(1); } - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + // Release multilayer block temporarily buffer. + BlockMultilayer.release(); + + String[] array = Config.PATHFIND_BUFFERS.split(";"); + _buffers = new BufferHolder[array.length]; + + int count = 0; + for (int i = 0; i < array.length; i++) + { + String buf = array[i]; + String[] args = buf.split("x"); + + try + { + int size = Integer.parseInt(args[1]); + count += size; + _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); + } + catch (Exception e) + { + LOGGER.warning("Could not load buffer setting:" + buf + ". " + e); + } + } + LOGGER.info("Loaded " + count + " node buffers."); + // Avoid wrong configs when no files are loaded. if ((loaded == 0) && Config.PATHFINDING) { @@ -118,616 +138,913 @@ public class GeoEngine } /** - * @param geoX - * @param geoY - * @return the region + * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer and log this situation. + * @param size : pre-calculated minimal required size + * @return NodeBuffer : buffer */ - private IRegion getRegion(int geoX, int geoY) + private NodeBuffer getBuffer(int size) { - final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); - if ((region < 0) || (region >= _regions.length())) + NodeBuffer current = null; + for (BufferHolder holder : _buffers) { - return null; - } - return _regions.get(region); - } - - /** - * @param filePath - * @param regionX - * @param regionY - * @throws IOException - */ - 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()).load().order(ByteOrder.LITTLE_ENDIAN))); - } - } - - /** - * @param regionX - * @param regionY - */ - public void unloadRegion(int regionX, int regionY) - { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); - } - - /** - * @param geoX - * @param geoY - * @return if geodata exist - */ - public boolean hasGeoPos(int geoX, int geoY) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return false; - } - return region.hasGeo(); - } - - /** - * Checks the specified position for available geodata. - * @param x the world x - * @param y the world y - * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise - */ - public boolean hasGeo(int x, int y) - { - return hasGeoPos(getGeoX(x), getGeoY(y)); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe check - */ - public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return true; - } - return region.checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe anti-corner cut - */ - public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) - { - boolean can = true; - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - return can && checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the nearest Z value - */ - public int getNearestZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNearestZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next lower Z value - */ - public int getNextLowerZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextLowerZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next higher Z value - */ - public int getNextHigherZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextHigherZ(geoX, geoY, worldZ); - } - - /** - * Gets the Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHeight(int x, int y, int z) - { - return getNearestZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next lower Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getLowerHeight(int x, int y, int z) - { - return getNextLowerZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next higher Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHigherHeight(int x, int y, int z) - { - return getNextHigherZ(getGeoX(x), getGeoY(y), z); - } - - /** - * @param worldX - * @return the geo X - */ - public int getGeoX(int worldX) - { - return (worldX - WORLD_MIN_X) / 16; - } - - /** - * @param worldY - * @return the geo Y - */ - public int getGeoY(int worldY) - { - return (worldY - WORLD_MIN_Y) / 16; - } - - /** - * @param worldZ - * @return the geo Z - */ - public int getGeoZ(int worldZ) - { - return (worldZ - WORLD_MIN_Z) / 16; - } - - /** - * @param geoX - * @return the world X - */ - public int getWorldX(int geoX) - { - return (geoX * 16) + WORLD_MIN_X + 8; - } - - /** - * @param geoY - * @return the world Y - */ - public int getWorldY(int geoY) - { - return (geoY * 16) + WORLD_MIN_Y + 8; - } - - /** - * @param geoZ - * @return the world Z - */ - public int getWorldZ(int geoZ) - { - return (geoZ * 16) + WORLD_MIN_Z + 8; - } - - /** - * Can see target. Doors as target always return true. Checks doors between. - * @param cha the character - * @param target the target - * @return {@code true} if the character can see the target (LOS), {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, WorldObject target) - { - if (target.isDoor()) - { - // Can always see doors. - return true; - } - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); - } - - /** - * Can see target. Checks doors between. - * @param cha the character - * @param worldPosition the world position - * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) - { - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param world - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @param tworld the target's instanceId - * @return - */ - public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) - { - return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instance - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) - { - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) - { - return false; - } - return canSeeTarget(x, y, z, tx, ty, tz); - } - - /** - * @param prevX - * @param prevY - * @param prevGeoZ - * @param curX - * @param curY - * @param nswe - * @return the LOS Z value - */ - private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) - { - if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) - { - throw new RuntimeException("Multiple directions!"); - } - - if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) - { - return getNearestZ(curX, curY, prevGeoZ); - } - - return getNextHigherZ(curX, curY, prevGeoZ); - } - - /** - * Can see target. Does not check doors between. - * @param xValue the x coordinate - * @param yValue the y coordinate - * @param zValue the z coordinate - * @param txValue the target's x coordinate - * @param tyValue the target's y coordinate - * @param tzValue the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) - { - int x = xValue; - int y = yValue; - int tx = txValue; - int ty = tyValue; - - int geoX = getGeoX(x); - int geoY = getGeoY(y); - int tGeoX = getGeoX(tx); - int tGeoY = getGeoY(ty); - - int z = getNearestZ(geoX, geoY, zValue); - int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - // Fastpath. - if ((geoX == tGeoX) && (geoY == tGeoY)) - { - if (hasGeoPos(tGeoX, tGeoY)) - { - return z == tz; - } - return true; - } - - if (tz > z) - { - int tmp = tx; - tx = x; - x = tmp; - - tmp = ty; - ty = y; - y = tmp; - - tmp = tz; - tz = z; - z = tmp; - - tmp = tGeoX; - tGeoX = geoX; - geoX = tmp; - - tmp = tGeoY; - tGeoY = geoY; - geoY = tmp; - } - - final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); - // First point is guaranteed to be available, skip it, we can always see our own position. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - final int prevZ = pointIter.z(); - int prevGeoZ = prevZ; - int ptIndex = 0; - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - - if ((curX == prevX) && (curY == prevY)) + // Find proper size of buffer. + if (holder._size < size) { continue; } - final int beeCurZ = pointIter.z(); - int curGeoZ = prevGeoZ; - - // Check if the position has geodata. - if (hasGeoPos(curX, curY)) + // Find unlocked NodeBuffer. + for (NodeBuffer buffer : holder._buffer) { - final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); - curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); - int maxHeight; - if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) + if (!buffer.isLocked()) { - maxHeight = z + MAX_SEE_OVER_HEIGHT; - } - else - { - maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; + continue; } - boolean canSeeThrough = false; - if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) + return buffer; + } + + LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); + + // NodeBuffer not found, allocate temporary buffer. + current = new NodeBuffer(holder._size); + current.isLocked(); + } + + return current; + } + + /** + * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + * @return boolean : True, when geodata file was loaded without problem. + */ + private boolean loadGeoBlocks(int regionX, int regionY) + { + final String filename = String.format(Config.GEODATA_TYPE.getFilename(), regionX, regionY); + final String filepath = Config.GEODATA_PATH + "\\" + filename; + + // Standard load. + try (RandomAccessFile raf = new RandomAccessFile(filepath, "r"); + FileChannel fc = raf.getChannel()) + { + // Initialize file buffer. + MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // Load 18B header for L2OFF geodata (1st and 2nd byte...region X and Y). + if (Config.GEODATA_TYPE == GeoType.L2OFF) + { + for (int i = 0; i < 18; i++) { - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) + buffer.get(); + } + } + + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Loop over region blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + if (Config.GEODATA_TYPE == GeoType.L2J) { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) - { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); + // Get block type. + final byte type = buffer.get(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + case GeoStructure.TYPE_MULTILAYER_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + default: + { + throw new IllegalArgumentException("Unknown block type: " + type); + } + } } else { - canSeeThrough = true; + // Get block type. + final short type = buffer.getShort(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + default: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + } } } + } + + // Check data consistency. + if (buffer.remaining() > 0) + { + LOGGER.warning("Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); + } + + // Loading was successful. + return true; + } + catch (Exception e) + { + // An error occured while loading, load null blocks. + LOGGER.warning("Error loading " + filename + " region file. " + e); + + // Replace whole region file with null blocks. + loadNullBlocks(regionX, regionY); + + // Loading was not successful. + return false; + } + } + + /** + * Loads null blocks. Used when no region file is detected or an error occurs during loading. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + */ + private void loadNullBlocks(int regionX, int regionY) + { + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Load all null blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + _blocks[blockX + ix][blockY + iy] = _nullBlock; + } + } + } + + /** + * Converts world X to geodata X. + * @param worldX + * @return int : Geo X + */ + public static int getGeoX(int worldX) + { + return (worldX - World.WORLD_X_MIN) >> 4; + } + + /** + * Converts world Y to geodata Y. + * @param worldY + * @return int : Geo Y + */ + public static int getGeoY(int worldY) + { + return (worldY - World.WORLD_Y_MIN) >> 4; + } + + /** + * Converts geodata X to world X. + * @param geoX + * @return int : World X + */ + public static int getWorldX(int geoX) + { + return (geoX << 4) + World.WORLD_X_MIN + 8; + } + + /** + * Converts geodata Y to world Y. + * @param geoY + * @return int : World Y + */ + public static int getWorldY(int geoY) + { + return (geoY << 4) + World.WORLD_Y_MIN + 8; + } + + /** + * Returns block of geodata on given coordinates. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return {@link ABlock} : Block of geodata. + */ + public ABlock getBlock(int geoX, int geoY) + { + final int x = geoX / GeoStructure.BLOCK_CELLS_X; + if ((x < 0) || (x >= GeoStructure.GEO_BLOCKS_X)) + { + return null; + } + final int y = geoY / GeoStructure.BLOCK_CELLS_Y; + if ((y < 0) || (y >= GeoStructure.GEO_BLOCKS_Y)) + { + return null; + } + return _blocks[x][y]; + } + + /** + * Check if geo coordinates has geo. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return boolean : True, if given geo coordinates have geodata + */ + public boolean hasGeoPos(int geoX, int geoY) + { + final ABlock block = getBlock(geoX, geoY); + return (block != null) && block.hasGeoPos(); + } + + /** + * Returns the height of cell, which is closest to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell geodata Z coordinate, closest to given coordinates. + */ + public short getHeightNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return (short) worldZ; + } + return block.getHeightNearest(geoX, geoY, worldZ); + } + + /** + * Returns the NSWE flag byte of cell, which is closes to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. + */ + public byte getNsweNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return GeoStructure.CELL_FLAG_ALL; + } + return block.getNsweNearest(geoX, geoY, worldZ); + } + + /** + * Check if world coordinates has geo. + * @param worldX : World X + * @param worldY : World Y + * @return boolean : True, if given world coordinates have geodata + */ + public boolean hasGeo(int worldX, int worldY) + { + return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param loc : The location used as reference. + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(Location loc) + { + return getHeightNearest(getGeoX(loc.getX()), getGeoY(loc.getY()), loc.getZ()); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param worldX : world x + * @param worldY : world y + * @param worldZ : world z + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(int worldX, int worldY, int worldZ) + { + return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); + } + + /** + * Check line of sight from {@link WorldObject} to {@link WorldObject}.
+ * @param object : The origin object. + * @param target : The target object. + * @return True, when object can see target. + */ + public boolean canSeeTarget(WorldObject object, WorldObject target) + { + // Can always see doors. + if (target.isDoor()) + { + return true; + } + + if (object.getInstanceWorld() != target.getInstanceWorld()) + { + return false; + } + + if (DoorData.getInstance().checkIfDoorsBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld())) + { + return false; + } + + // Get object's and target's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + double theight = 0; + if (target instanceof Creature) + { + theight += (((Creature) target).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + return canSee(object.getX(), object.getY(), object.getZ(), oheight, target.getX(), target.getY(), target.getZ(), theight) && canSee(target.getX(), target.getY(), target.getZ(), theight, object.getX(), object.getY(), object.getZ(), oheight); + } + + /** + * Check line of sight from {@link WorldObject} to {@link Location}.
+ * Note: The check uses {@link Location}'s real Z coordinate (e.g. point above ground), not its geodata representation. + * @param object : The origin object. + * @param position : The target position. + * @return True, when object can see position. + */ + public boolean canSeeLocation(WorldObject object, Location position) + { + // Get object and location coordinates. + int ox = object.getX(); + int oy = object.getY(); + int oz = object.getZ(); + int tx = position.getX(); + int ty = position.getY(); + int tz = position.getZ(); + + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld())) + { + return false; + } + + // Get object's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + // Perform geodata check. + return canSee(ox, oy, oz, oheight, tx, ty, tz, 0) && canSee(tx, ty, tz, 0, ox, oy, oz, oheight); + } + + /** + * Simple check for origin to target visibility.
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param oheight : The height of origin, used as start point. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param theight : The height of target, used as end point. + * @return True, when origin can see target. + */ + public boolean canSee(int ox, int oy, int oz, double oheight, int tx, int ty, int tz, double theight) + { + // Get origin geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get target geodata coordinates. + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check being on same cell and layer (index). + // Note: Get index must use origin height increased by cell height, the method returns index to height exclusive self. + int index = block.getIndexBelow(gox, goy, oz + GeoStructure.CELL_HEIGHT); + if (index < 0) + { + return false; + } + + if ((gox == gtx) && (goy == gty)) + { + return index == block.getIndexBelow(gtx, gty, tz + GeoStructure.CELL_HEIGHT); + } + + // Get ground and nswe flag. + int groundZ = block.getHeight(index); + int nswe = block.getNswe(index); + + // Get delta coordinates, slope of line (XY, XZ) and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double dz = (tz + theight) - (oz + oheight); + final double m = (double) dy / dx; + final double mz = dz / Math.sqrt((dx * dx) + (dy * dy)); + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); + + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) + { + // Set next cell in X direction. + gridX += mdt.getStepX(); + gox += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); - if (!canSeeThrough) - { - return false; - } + // Set next cell in Y direction. + gridY += mdt.getStepY(); + goy += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevGeoZ = curGeoZ; - ++ptIndex; + // Get block of the next cell. + block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get line of sight height (including Z slope). + double losz = oz + oheight + Config.MAX_OBSTACLE_HEIGHT; + losz += mz * Math.sqrt(((checkX - ox) * (checkX - ox)) + ((checkY - oy) * (checkY - oy))); + + // Check line of sight going though wall (vertical check). + + // Get index of particular layer, based on last iterated cell conditions. + boolean canMove = (nswe & dir) != 0; + if (canMove) + { + // No wall present, get next cell below current cell. + index = block.getIndexBelow(gox, goy, groundZ + GeoStructure.CELL_IGNORE_HEIGHT); + } + else + { + // Wall present, get next cell above current cell. + index = block.getIndexAbove(gox, goy, groundZ - (2 * GeoStructure.CELL_HEIGHT)); + } + + // Next cell's does not exist (no geodata with valid condition), return fail. + if (index < 0) + { + return false; + } + + // Get next cell's layer height. + int z = block.getHeight(index); + + // Perform sine of sight check (next cell is above line of sight line), return fail. + if (z > losz) + { + return false; + } + + // Next cell is accessible, update z and NSWE. + groundZ = z; + nswe = block.getNswe(index); } + // Iteration is completed, no obstacle is found. return true; } /** - * Move check. - * @param x the x coordinate - * @param y the y coordinate - * @param zValue the z coordinate - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tzValue the target's z coordinate - * @param instance the instance - * @return the last Location (x,y,z) where player can walk - just before wall + * Check movement from coordinates to coordinates.
+ * Note: The Z coordinates are supposed to be already validated geodata coordinates. + * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return True, when target coordinates are reachable from origin coordinates. */ - public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(x); - final int geoY = getGeoY(y); - final int z = getNearestZ(geoX, geoY, zValue); - final int tGeoX = getGeoX(tx); - final int tGeoY = getGeoY(ty); - final int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return new Location(x, y, getHeight(x, y, z)); - } - if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) - { - return new Location(x, y, getHeight(x, y, z)); + return false; } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = z; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return false; + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + int goz = getHeightNearest(gox, goy, oz); + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check movement within same cell. + if ((gox == gtx) && (goy == gty)) + { + return goz == getHeight(tx, ty, tz); + } + + // Get nswe flag. + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - // Can't move, return previous location. - return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check point heading into obstacle, if so return current point. + if ((nswe & dir) == 0) + { + return false; + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return false; + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != tz)) - { - // Different floors, return start location. - return new Location(x, y, z); - } - - return new Location(tx, ty, tz); + // When origin Z is target Z, the move is successful. + return goz == getHeight(tx, ty, tz); } /** - * Checks if its possible to move from one location to another. - * @param fromX the X coordinate to start checking from - * @param fromY the Y coordinate to start checking from - * @param fromZvalue the Z coordinate to start checking from - * @param toX the X coordinate to end checking at - * @param toY the Y coordinate to end checking at - * @param toZvalue the Z coordinate to end checking at - * @param instance the instance - * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + * Check movement from origin to target coordinates. Returns last available point in the checked path.
+ * Target X and Y reachable and Z is on same floor: + *
    + *
  • Location of the target with corrected Z value from geodata.
  • + *
+ * Target X and Y reachable but Z is on another floor: + *
    + *
  • Location of the origin with corrected Z value from geodata.
  • + *
+ * Target X and Y not reachable: + *
    + *
  • Last accessible location in destination to target.
  • + *
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return The {@link Location} representing last point of movement (e.g. just before wall). */ - public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + public Location getValidLocation(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(fromX); - final int geoY = getGeoY(fromY); - final int fromZ = getNearestZ(geoX, geoY, fromZvalue); - final int tGeoX = getGeoX(toX); - final int tGeoY = getGeoY(toY); - final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); - - if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) - { - return false; + return new Location(ox, oy, oz); } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = fromZ; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return new Location(ox, oy, oz); + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + final int gtz = getHeightNearest(gtx, gty, tz); + int goz = getHeightNearest(gox, goy, oz); + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - return false; - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check target cell is out of geodata grid (world coordinates). + if ((nx < 0) || (nx >= GeoStructure.GEO_CELLS_X) || (ny < 0) || (ny >= GeoStructure.GEO_CELLS_Y)) + { + return new Location(checkX, checkY, goz); + } + + // Check point heading into obstacle, if so return current (border) point. + if ((nswe & dir) == 0) + { + return new Location(checkX, checkY, goz); + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current (border) point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return new Location(checkX, checkY, goz); + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) - { - // Different floors. - return false; - } - - return true; + // Compare Z coordinates: + // If same, path is okay, return target point and fix its Z geodata coordinate. + // If not same, path is does not exist, return origin point. + return goz == gtz ? new Location(tx, ty, gtz) : new Location(ox, oy, oz); } + /** + * Returns the list of location objects as a result of complete path calculation. + * @param ox : origin x + * @param oy : origin y + * @param oz : origin z + * @param tx : target x + * @param ty : target y + * @param tz : target z + * @param instance + * @return {@code List} : complete path from nodes + */ + public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + { + // Get origin and check existing geo coords. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + if (!hasGeoPos(gox, goy)) + { + return Collections.emptyList(); + } + + int goz = getHeightNearest(gox, goy, oz); + + // Get target and check existing geo coords. + int gtx = getGeoX(tx); + int gty = getGeoY(ty); + if (!hasGeoPos(gtx, gty)) + { + return Collections.emptyList(); + } + + int gtz = getHeightNearest(gtx, gty, tz); + + // Prepare buffer for pathfinding calculations. + NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + if (buffer == null) + { + return Collections.emptyList(); + } + + // Find path. + List path = null; + try + { + path = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + if (path.isEmpty()) + { + return Collections.emptyList(); + } + } + catch (Exception e) + { + return Collections.emptyList(); + } + finally + { + buffer.free(); + } + + // Check path. + if (path.size() < 3) + { + return path; + } + + // Get path list iterator. + ListIterator point = path.listIterator(); + + // Get node A (origin). + int nodeAx = ox; + int nodeAy = oy; + int nodeAz = goz; + + // Get node B. + Location nodeB = point.next(); + + // Iterate thought the path to optimize it. + while (point.hasNext()) + { + // Get node C. + Location nodeC = path.get(point.nextIndex()); + + // Check movement from node A to node C. + if (canMoveToTarget(nodeAx, nodeAy, nodeAz, nodeC.getX(), nodeC.getY(), nodeC.getZ(), instance)) + { + // Can move from node A to node C. + // Remove node B. + point.remove(); + } + else + { + // Can not move from node A to node C. + // Set node A (node B is part of path, update A coordinates). + nodeAx = nodeB.getX(); + nodeAy = nodeB.getY(); + nodeAz = nodeB.getZ(); + } + + // Set node B. + nodeB = point.next(); + } + + return path; + } + + /** + * NodeBuffer container with specified size and count of separate buffers. + */ + private static class BufferHolder + { + final int _size; + final List _buffer; + + public BufferHolder(int size, int count) + { + _size = size; + _buffer = new ArrayList<>(count); + + for (int i = 0; i < count; i++) + { + _buffer.add(new NodeBuffer(size)); + } + } + } + + /** + * Returns the instance of the {@link GeoEngine}. + * @return {@link GeoEngine} : The instance. + */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -737,4 +1054,4 @@ public class GeoEngine { protected static final GeoEngine INSTANCE = new GeoEngine(); } -} +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java deleted file mode 100644 index cc76b7523a..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ /dev/null @@ -1,301 +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 . - */ -package org.l2jmobius.gameserver.geoengine; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; -import org.l2jmobius.gameserver.model.World; -import org.l2jmobius.gameserver.model.instancezone.Instance; - -/** - * @author -Nemesiss- - */ -public class GeoEnginePathfinding -{ - private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); - - private BufferInfo[] _buffers; - - protected GeoEnginePathfinding() - { - try - { - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - - _buffers = 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); - } - - _buffers[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"); - } - } - - public boolean pathNodesExist(short regionoffset) - { - return false; - } - - public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) - { - 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)))); - if (buffer == null) - { - return null; - } - - List path = null; - try - { - final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); - - if (result == null) - { - return null; - } - - path = constructPath(result); - } - catch (Exception e) - { - LOGGER.warning(e.getMessage()); - return null; - } - finally - { - buffer.free(); - } - - // check path - if (path.size() < 3) - { - return path; - } - - int currentX, currentY, currentZ; - ListIterator middlePoint; - - middlePoint = path.listIterator(); - currentX = x; - currentY = y; - currentZ = z; - - while (middlePoint.hasNext()) - { - final AbstractNodeLoc locMiddle = middlePoint.next(); - if (!middlePoint.hasNext()) - { - break; - } - - final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); - if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) - { - middlePoint.remove(); - } - else - { - currentX = locMiddle.getX(); - currentY = locMiddle.getY(); - currentZ = locMiddle.getZ(); - } - } - - return path; - } - - /** - * Convert geodata position to pathnode position - * @param geo_pos - * @return pathnode position - */ - public short getNodePos(int geo_pos) - { - return (short) (geo_pos >> 3); // OK? - } - - /** - * Convert node position to pathnode block position - * @param node_pos - * @return pathnode block position (0...255) - */ - public short getNodeBlock(int node_pos) - { - return (short) (node_pos % 256); - } - - public byte getRegionX(int node_pos) - { - return (byte) ((node_pos >> 8) + World.TILE_X_MIN); - } - - public byte getRegionY(int node_pos) - { - return (byte) ((node_pos >> 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 node_x rx - * @return - */ - public int calculateWorldX(short node_x) - { - return World.MAP_MIN_X + (node_x * 128) + 48; - } - - /** - * Convert pathnode y to World y position - * @param node_y - * @return - */ - public int calculateWorldY(short node_y) - { - return World.MAP_MIN_Y + (node_y * 128) + 48; - } - - private List constructPath(AbstractNode nodeValue) - { - final LinkedList path = new LinkedList<>(); - int previousDirectionX = Integer.MIN_VALUE; - int previousDirectionY = Integer.MIN_VALUE; - int directionX, directionY; - - AbstractNode node = nodeValue; - while (node.getParent() != null) - { - directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); - directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); - - // only add a new route point if moving direction changes - if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) - { - previousDirectionX = directionX; - previousDirectionY = directionY; - - path.addFirst(node.getLoc()); - node.setLoc(null); - } - - node = node.getParent(); - } - - return path; - } - - private CellNodeBuffer alloc(int size) - { - CellNodeBuffer current = null; - for (BufferInfo i : _buffers) - { - if (i.mapSize >= size) - { - for (CellNodeBuffer buf : i.buffer) - { - if (buf.lock()) - { - current = buf; - break; - } - } - if (current != null) - { - break; - } - - // not found, allocate temporary buffer - current = new CellNodeBuffer(i.mapSize); - current.lock(); - if (i.buffer.size() < i.count) - { - i.buffer.add(current); - break; - } - } - } - - return current; - } - - private static final class BufferInfo - { - final int mapSize; - final int count; - ArrayList buffer; - - public BufferInfo(int size, int cnt) - { - mapSize = size; - count = cnt; - buffer = new ArrayList<>(count); - } - } - - public static GeoEnginePathfinding getInstance() - { - return SingletonHolder.INSTANCE; - } - - private static class SingletonHolder - { - protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); - } -} diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java new file mode 100644 index 0000000000..49ec35aa1c --- /dev/null +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java @@ -0,0 +1,85 @@ +/* + * 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 . + */ +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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @param index : Index of the cell. + * @return short : Cell geodata Z coordinate, below given coordinates. + */ + public abstract byte getNswe(int index); +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java new file mode 100644 index 0000000000..f5997bf287 --- /dev/null +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java @@ -0,0 +1,130 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java new file mode 100644 index 0000000000..9af9d2a28a --- /dev/null +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java @@ -0,0 +1,95 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java new file mode 100644 index 0000000000..31fbf5d477 --- /dev/null +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java new file mode 100644 index 0000000000..f77d21d84b --- /dev/null +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java @@ -0,0 +1,68 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +public class BlockNull extends ABlock +{ + @Override + public boolean hasGeoPos() + { + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java deleted file mode 100644 index 61ea4fcf82..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java +++ /dev/null @@ -1,48 +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 . - */ -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() - { - } -} diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java deleted file mode 100644 index 3c8de1a7f7..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java +++ /dev/null @@ -1,77 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java deleted file mode 100644 index 1ccdefe3da..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java +++ /dev/null @@ -1,56 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java new file mode 100644 index 0000000000..691b164e0c --- /dev/null +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ +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_ALL = 0x0F; + + // 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; +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java deleted file mode 100644 index 25eafc5a04..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java +++ /dev/null @@ -1,42 +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 . - */ -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); -} diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java deleted file mode 100644 index 0d2c765002..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java +++ /dev/null @@ -1,47 +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 . - */ -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(); -} diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java deleted file mode 100644 index 52df457360..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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); - - // 1 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; - } -} diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java deleted file mode 100644 index 72a5a635c7..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java +++ /dev/null @@ -1,55 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author HorridoJoho - */ -public final class NullRegion implements IRegion -{ - public static final NullRegion INSTANCE = new NullRegion(); - - @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 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; - } -} diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java deleted file mode 100644 index fc924cb875..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java +++ /dev/null @@ -1,92 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java deleted file mode 100644 index 5087513ed9..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java +++ /dev/null @@ -1,87 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public abstract class AbstractNode -{ - private T _loc; - private AbstractNode _parent; - - public AbstractNode(T loc) - { - _loc = loc; - } - - public void setParent(AbstractNode p) - { - _parent = p; - } - - public AbstractNode getParent() - { - return _parent; - } - - public T getLoc() - { - return _loc; - } - - public void setLoc(T l) - { - _loc = l; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); - return result; - } - - @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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java deleted file mode 100644 index 7223b5b2be..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java +++ /dev/null @@ -1,33 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -/** - * @author -Nemesiss- - */ -public abstract class AbstractNodeLoc -{ - public abstract int getX(); - - public abstract int getY(); - - public abstract int getZ(); - - public abstract int getNodeX(); - - public abstract int getNodeY(); -} diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java deleted file mode 100644 index 3425234e0e..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public class CellNode extends AbstractNode -{ - 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java deleted file mode 100644 index 522610bb7c..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java +++ /dev/null @@ -1,348 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -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 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) - { - _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(); - } - - public final List debugPath() - { - final List 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 void getNeighbors() - { - if (!_current.getLoc().canGoAll()) - { - 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); - } - - // SouthEast - if ((nodeE != null) && (nodeS != null)) - { - if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) - { - addNode(x + 1, y + 1, z, true); - } - } - - // SouthWest - if ((nodeS != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) - { - addNode(x - 1, y + 1, z, true); - } - } - - // NorthEast - if ((nodeN != null) && (nodeE != null)) - { - if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) - { - addNode(x + 1, y - 1, z, true); - } - } - - // NorthWest - if ((nodeN != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) - { - addNode(x - 1, y - 1, z, true); - } - } - } - - private 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(); - // reinit node if needed - if (result.getLoc() != null) - { - result.getLoc().set(x, y, z); - } - else - { - result.setLoc(new NodeLoc(x, y, z)); - } - } - - return result; - } - - private 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 boolean isHighWeight(int x, int y, int z) - { - final CellNode result = getNode(x, y, z); - if (result == null) - { - return true; - } - - if (!result.getLoc().canGoAll()) - { - return true; - } - if (Math.abs(result.getLoc().getZ() - z) > 16) - { - return true; - } - - return false; - } - - private 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java new file mode 100644 index 0000000000..fa5ca43c2f --- /dev/null +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -0,0 +1,111 @@ +/* + * 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 . + */ +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 geodata values. + private int _geoX; + private int _geoY; + private byte _nswe; + + // 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; + + _costG = 0; + _costH = 0; + _costF = 0; + + _parent = null; + } + + public void setGeo(int gx, int gy, int gz, byte nswe) + { + super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); + + _geoX = gx; + _geoY = gy; + _nswe = nswe; + } + + 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 int getCostF() + { + return _costF; + } + + public Node getParent() + { + return _parent; + } + + @Override + public int compareTo(Node o) + { + return _costF - o._costF; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java new file mode 100644 index 0000000000..b464212c96 --- /dev/null +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -0,0 +1,368 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.awt.Color; +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; +import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; + +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 _opened; + // Container holding Nodes already explored. + private final List _closed; + + // Target coordinates. + private int _gtx; + private int _gty; + private int _gtz; + + // Pathfinding statistics. + private long _timeStamp; + private long _lastElapsedTime; + + 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 findPath(int gox, int goy, int goz, int gtx, int gty, int gtz) + { + // Set start timestamp. + _timeStamp = System.currentTimeMillis(); + + // 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)); + _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 constructPath() + { + // Create result. + final LinkedList 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; + } + + /** + * Creates list of Nodes to show debug path. + * @param debug : The debug packet to add debug informations in. + */ + public void debugPath(ExServerPrimitive debug) + { + // Add all opened node as yellow points. + for (Node n : _opened) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); + } + + // Add all opened node as blue points. + for (Node n : _closed) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); + } + } + + 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; + + _lastElapsedTime = System.currentTimeMillis() - _timeStamp; + _lock.unlock(); + } + + public long getElapsedTime() + { + return _lastElapsedTime; + } + + /** + * Expand the current {@link Node} by exploring its neighbors (axially and diagonally). + */ + private void expand() + { + // Movement is blocked, skip. + final byte nswe = _current.getNSWE(); + if (nswe == 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; + + // Can move north, expand. + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + } + + // Can move south, expand. + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + } + + // Can move west, expand. + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move east, expand. + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move north-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move north-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + } + + /** + * 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 weight : The weight of movement to the new node. + * @return The nswe of the added node. Blank, if not added. + */ + private byte addNode(int gx, int gy, int gzValue, int weight) + { + // 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]; + + // Set node geodata coordinates. + node.setGeo(gx, gy, gz, nswe); + + // 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, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + _opened.add(node); + _bufferIndex++; + return nswe; + } + + /** + * Calculate cost H value, calculated using diagonal distance method.
+ * 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. + final int dx = Math.abs(gx - _gtx); + final int dy = Math.abs(gy - _gty); + final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + + // Get diagonal and axial differences to the target. + final int dd = Math.min(dx, dy); + final int da = Math.max(dx, dy) - dd; + + // Calculate the diagonal distance of the node to the target. + return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java deleted file mode 100644 index e3bad04b25..0000000000 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; - -/** - * @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 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 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; - // return super.hashCode(); - } - - @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; - if (_x != other._x) - { - return false; - } - if (_y != other._y) - { - return false; - } - if (_goNorth != other._goNorth) - { - return false; - } - if (_goEast != other._goEast) - { - return false; - } - if (_goSouth != other._goSouth) - { - return false; - } - if (_goWest != other._goWest) - { - return false; - } - if (_geoHeight != other._geoHeight) - { - return false; - } - return true; - } -} diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java index fec7e24f5e..c93649d8fd 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java @@ -94,15 +94,15 @@ public class ZoneManager implements IXmlReader private static final Map SETTINGS = new HashMap<>(); private static final int SHIFT_BY = 15; - private static final int OFFSET_X = Math.abs(World.MAP_MIN_X >> SHIFT_BY); - private static final int OFFSET_Y = Math.abs(World.MAP_MIN_Y >> SHIFT_BY); + private static final int OFFSET_X = Math.abs(World.WORLD_X_MIN >> SHIFT_BY); + private static final int OFFSET_Y = Math.abs(World.WORLD_Y_MIN >> SHIFT_BY); private final Map, ConcurrentHashMap> _classZones = new ConcurrentHashMap<>(); private final Map _spawnTerritories = new ConcurrentHashMap<>(); private final AtomicInteger _lastDynamicId = new AtomicInteger(300000); private List _debugItems; - private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.MAP_MAX_X >> SHIFT_BY) + OFFSET_X + 1][(World.MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y + 1]; + private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.WORLD_X_MAX >> SHIFT_BY) + OFFSET_X + 1][(World.WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y + 1]; /** * Instantiates a new zone manager. diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/Location.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/Location.java index 73689ab1a6..ca3b22a3f0 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/Location.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/Location.java @@ -16,30 +16,30 @@ */ package org.l2jmobius.gameserver.model; +import java.util.Objects; + +import org.l2jmobius.commons.util.Point2D; import org.l2jmobius.gameserver.model.interfaces.ILocational; import org.l2jmobius.gameserver.model.interfaces.IPositionable; /** - * Location data transfer object.
- * Contains coordinates data, heading and instance Id. - * @author Zoey76 + * A datatype used to retain a 3D (x/y/z/heading) point. It got the capability to be set and cleaned. */ -public class Location implements IPositionable +public class Location extends Point2D implements IPositionable { - protected int _x; - protected int _y; - protected int _z; - private int _heading; + protected volatile int _z; + protected volatile int _heading; public Location(int x, int y, int z) { - this(x, y, z, 0); + super(x, y); + _z = z; + _heading = 0; } public Location(int x, int y, int z, int heading) { - _x = x; - _y = y; + super(x, y); _z = z; _heading = heading; } @@ -51,9 +51,8 @@ public class Location implements IPositionable public Location(StatSet set) { - _x = set.getInt("x"); - _y = set.getInt("y"); - _z = set.getInt("z"); + super(set.getInt("x", 0), set.getInt("y", 0)); + _z = set.getInt("z", 0); _heading = set.getInt("heading", 0); } @@ -146,6 +145,25 @@ public class Location implements IPositionable _heading = loc.getHeading(); } + @Override + public void clean() + { + super.clean(); + _z = 0; + } + + @Override + public Location clone() + { + return new Location(_x, _y, _z); + } + + @Override + public int hashCode() + { + return (31 * super.hashCode()) + Objects.hash(_z); + } + @Override public boolean equals(Object obj) { diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/World.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/World.java index c1510e8c0e..0d796dd834 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/World.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/World.java @@ -66,19 +66,19 @@ public class World public static final int TILE_Y_MAX = 26; public static final int TILE_ZERO_COORD_X = 20; public static final int TILE_ZERO_COORD_Y = 18; - public static final int MAP_MIN_X = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; - public static final int MAP_MIN_Y = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; + public static final int WORLD_X_MIN = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; + public static final int WORLD_Y_MIN = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; - public static final int MAP_MAX_X = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; - public static final int MAP_MAX_Y = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; + public static final int WORLD_X_MAX = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; + public static final int WORLD_Y_MAX = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; /** Calculated offset used so top left region is 0,0 */ - public static final int OFFSET_X = Math.abs(MAP_MIN_X >> SHIFT_BY); - public static final int OFFSET_Y = Math.abs(MAP_MIN_Y >> SHIFT_BY); + public static final int OFFSET_X = Math.abs(WORLD_X_MIN >> SHIFT_BY); + public static final int OFFSET_Y = Math.abs(WORLD_Y_MIN >> SHIFT_BY); /** Number of regions. */ - private static final int REGIONS_X = (MAP_MAX_X >> SHIFT_BY) + OFFSET_X; - private static final int REGIONS_Y = (MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y; + private static final int REGIONS_X = (WORLD_X_MAX >> SHIFT_BY) + OFFSET_X; + private static final int REGIONS_Y = (WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y; /** Map containing all the players in game. */ private static final Map _allPlayers = new ConcurrentHashMap<>(); @@ -817,9 +817,6 @@ public class World return _memberInPartyNumber.get(); } - /** - * @return the current instance of World - */ public static World getInstance() { return SingletonHolder.INSTANCE; diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/WorldObject.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/WorldObject.java index 0e5536bbc0..62313ffea1 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/WorldObject.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/WorldObject.java @@ -188,23 +188,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif synchronized (this) { int spawnX = x; - if (spawnX > World.MAP_MAX_X) + if (spawnX > World.WORLD_X_MAX) { - spawnX = World.MAP_MAX_X - 5000; + spawnX = World.WORLD_X_MAX - 5000; } - if (spawnX < World.MAP_MIN_X) + if (spawnX < World.WORLD_X_MIN) { - spawnX = World.MAP_MIN_X + 5000; + spawnX = World.WORLD_X_MIN + 5000; } int spawnY = y; - if (spawnY > World.MAP_MAX_Y) + if (spawnY > World.WORLD_Y_MAX) { - spawnY = World.MAP_MAX_Y - 5000; + spawnY = World.WORLD_Y_MAX - 5000; } - if (spawnY < World.MAP_MIN_Y) + if (spawnY < World.WORLD_Y_MIN) { - spawnY = World.MAP_MIN_Y + 5000; + spawnY = World.WORLD_Y_MIN + 5000; } // Set the x,y,z position of the WorldObject. If flagged with _isSpawned, setXYZ will automatically update world region, so avoid that. @@ -518,23 +518,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif public void setXYZInvisible(int x, int y, int z) { int correctX = x; - if (correctX > World.MAP_MAX_X) + if (correctX > World.WORLD_X_MAX) { - correctX = World.MAP_MAX_X - 5000; + correctX = World.WORLD_X_MAX - 5000; } - if (correctX < World.MAP_MIN_X) + if (correctX < World.WORLD_X_MIN) { - correctX = World.MAP_MIN_X + 5000; + correctX = World.WORLD_X_MIN + 5000; } int correctY = y; - if (correctY > World.MAP_MAX_Y) + if (correctY > World.WORLD_Y_MAX) { - correctY = World.MAP_MAX_Y - 5000; + correctY = World.WORLD_Y_MAX - 5000; } - if (correctY < World.MAP_MIN_Y) + if (correctY < World.WORLD_Y_MIN) { - correctY = World.MAP_MIN_Y + 5000; + correctY = World.WORLD_Y_MIN + 5000; } setXYZ(correctX, correctY, z); diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/Creature.java index 61e2d56016..a8b6e52f2f 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -66,8 +66,6 @@ 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.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -2586,7 +2584,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3418,8 +3416,8 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe final int originalX = x; final int originalY = y; final int originalZ = z; - final int gtx = (originalX - World.MAP_MIN_X) >> 4; - final int gty = (originalY - World.MAP_MIN_Y) >> 4; + final int gtx = (originalX - World.WORLD_X_MIN) >> 4; + final int gty = (originalY - World.WORLD_Y_MIN) >> 4; if (isOnGeodataPath()) { try @@ -3442,7 +3440,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe && !(((curZ - z) > 300) && (distance < 300))) // Prohibit correcting destination if character wants to fall. { // location different if destination wasn't reached (or just z coord is different) - final Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(curX, curY, curZ, x, y, z, getInstanceWorld()); + final Location destiny = GeoEngine.getInstance().getValidLocation(curX, curY, curZ, x, y, z, getInstanceWorld()); x = destiny.getX(); y = destiny.getY(); z = destiny.getZ(); @@ -3456,7 +3454,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { if (isPlayer() && !_isFlying && !isInWater) diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/Summon.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/Summon.java index 13103f203b..122aa8bea5 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/Summon.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/Summon.java @@ -106,7 +106,7 @@ public abstract class Summon extends Playable final int x = owner.getX(); final int y = owner.getY(); final int z = owner.getZ(); - final Location location = GeoEngine.getInstance().canMoveToTargetLoc(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, owner.getInstanceWorld()); + final Location location = GeoEngine.getInstance().getValidLocation(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, getInstanceWorld()); setXYZInvisible(location.getX(), location.getY(), location.getZ()); } diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index d6e00e47ef..b30883f1e4 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -14177,8 +14177,8 @@ public class PlayerInstance extends Playable public boolean isInTimedHuntingZone(int zoneId, int locX, int locY) { - final int x = ((locX - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((locY - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((locX - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((locY - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; switch (zoneId) { diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java index abaec9036f..20cd1966d5 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java @@ -1560,7 +1560,7 @@ public class ItemInstance extends WorldObject if (dropper != null) { final Instance instance = dropper.getInstanceWorld(); - final Location dropDest = GeoEngine.getInstance().canMoveToTargetLoc(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); + final Location dropDest = GeoEngine.getInstance().getValidLocation(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); x = dropDest.getX(); y = dropDest.getY(); z = dropDest.getZ(); diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java index 4cb3867712..8831cce6e5 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java @@ -1184,7 +1184,7 @@ public class SkillCaster implements Runnable } } - final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().canMoveToTargetLoc(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); + final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().getValidLocation(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); creature.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); creature.broadcastPacket(new FlyToLocation(creature, destination, flyType, 0, 0, 333)); diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java index c62c821f44..30068134e3 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java @@ -114,7 +114,7 @@ public class ValidatePosition implements IClientIncomingPacket { if (player.isFalling(_z)) { - final int nearestZ = GeoEngine.getInstance().getHigherHeight(_x, _y, _z); + final int nearestZ = GeoEngine.getInstance().getHeight(_x, _y, _z); if (player.getZ() < nearestZ) { player.setXYZ(_x, _y, nearestZ); diff --git a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/util/GeoUtils.java index 4600a0fc53..19a979819e 100644 --- a/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_Classic_3.0_TheKamael/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -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.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; @@ -30,21 +30,21 @@ public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getInstance().getWorldX(iter.x()); - final int wy = GeoEngine.getInstance().getWorldY(iter.y()); + final int wx = GeoEngine.getWorldX(iter.x()); + final int wy = GeoEngine.getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public final class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); - int wy = GeoEngine.getInstance().getWorldY(prevY); + int wx = GeoEngine.getWorldX(prevX); + int wy = GeoEngine.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.getInstance().getWorldX(curX); - wy = GeoEngine.getInstance().getWorldY(curY); + wx = GeoEngine.getWorldX(curX); + wy = GeoEngine.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().checkNearestNswe(x, y, z, nswe)) + if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0) { return Color.GREEN; } @@ -109,9 +109,8 @@ public final class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final GeoEngine ge = GeoEngine.getInstance(); - final int playerGx = ge.getGeoX(player.getX()); - final int playerGy = ge.getGeoY(player.getY()); + final int playerGx = GeoEngine.getGeoX(player.getX()); + final int playerGy = GeoEngine.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -135,32 +134,32 @@ public final class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = ge.getWorldX(gx); - final int y = ge.getWorldY(gy); - final int z = ge.getNearestZ(gx, gy, player.getZ()); + final int x = GeoEngine.getWorldX(gx); + final int y = GeoEngine.getWorldY(gy); + final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); + Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); 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, Cell.NSWE_EAST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); 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, Cell.NSWE_SOUTH); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); 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, Cell.NSWE_WEST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); 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); @@ -188,41 +187,41 @@ public final class GeoUtils { if (y > lastY) { - return Cell.NSWE_SOUTH_EAST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E; } else if (y < lastY) { - return Cell.NSWE_NORTH_EAST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E; } else { - return Cell.NSWE_EAST; + return GeoStructure.CELL_FLAG_E; } } else if (x < lastX) // west { if (y > lastY) { - return Cell.NSWE_SOUTH_WEST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W; } else if (y < lastY) { - return Cell.NSWE_NORTH_WEST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W; } else { - return Cell.NSWE_WEST; + return GeoStructure.CELL_FLAG_W; } } else // unchanged x { if (y > lastY) { - return Cell.NSWE_SOUTH; + return GeoStructure.CELL_FLAG_S; } else if (y < lastY) { - return Cell.NSWE_NORTH; + return GeoStructure.CELL_FLAG_N; } else { diff --git a/L2J_Mobius_Classic_Interlude/dist/game/config/GeoEngine.ini b/L2J_Mobius_Classic_Interlude/dist/game/config/GeoEngine.ini index 609e8a89a7..e740c678dd 100644 --- a/L2J_Mobius_Classic_Interlude/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Classic_Interlude/dist/game/config/GeoEngine.ini @@ -6,33 +6,44 @@ # at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ 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 + # ================================================================= -# Path finding +# Pathfinding # ================================================================= # 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 +# an alternative path (e.g. walk around obstacle), default: True +PathFinding = True -# Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 -PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 +# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 -# Weight for nodes without obstacles far from walls -LowWeight = 0.5 +# Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 +MoveWeight = 10 +MoveWeightDiag = 14 -# Weight for nodes near walls -MediumWeight = 2 +# 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 -# Weight for nodes with obstacles -HighWeight = 3 +# 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. -# Default: LowWeight * sqrt(2) -DiagonalWeight = 0.707 +# Maximum number of generated nodes per one path-finding process, default 3500 +MaxIterations = 3500 # ================================================================= -# Other +# Line of Sight # ================================================================= -# Correct player Z after falling through the ground. -CorrectPlayerZ = False +# 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 diff --git a/L2J_Mobius_Classic_Interlude/dist/game/data/geodata/L2D_GeoEngine.patch b/L2J_Mobius_Classic_Interlude/dist/game/data/geodata/L2D_GeoEngine.patch deleted file mode 100644 index f8577e3a0e..0000000000 --- a/L2J_Mobius_Classic_Interlude/dist/game/data/geodata/L2D_GeoEngine.patch +++ /dev/null @@ -1,6035 +0,0 @@ -Index: dist/game/GeoDataConverter.bat -=================================================================== ---- dist/game/GeoDataConverter.bat (nonexistent) -+++ dist/game/GeoDataConverter.bat (working copy) -@@ -0,0 +1,6 @@ -+@echo off -+title L2D geodata converter -+ -+java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter -+ -+pause -Index: dist/game/GeoDataConverter.sh -=================================================================== ---- dist/game/GeoDataConverter.sh (nonexistent) -+++ dist/game/GeoDataConverter.sh (working copy) -@@ -0,0 +1,4 @@ -+#! /bin/sh -+ -+java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 -+ -Index: dist/game/config/GeoEngine.ini -=================================================================== ---- dist/game/config/GeoEngine.ini (revision 8409) -+++ dist/game/config/GeoEngine.ini (working copy) -@@ -1,6 +1,10 @@ - # ================================================================= - # Geodata - # ================================================================= -+# Because of real-time performance we are using geodata files only in -+# diagonal L2D format now (using filename e.g. 22_16.l2d). -+# L2D geodata can be obtained by conversion of existing L2J or L2OFF geodata. -+# Launch "GeoDataConverter.bat/sh" and follow instructions to start the conversion. - - # 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/ -@@ -13,6 +17,16 @@ - CoordSynchronize = 2 - - # ================================================================= -+# Path checking -+# ================================================================= -+ -+# 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 finding - # ================================================================= - -@@ -23,19 +37,23 @@ - # Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - --# Weight for nodes without obstacles far from walls --LowWeight = 0.5 -+# Base path weight, when moving from one node to another on axis direction, default: 10 -+BaseWeight = 10 - --# Weight for nodes near walls --MediumWeight = 2 -+# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -+DiagonalWeight = 14 - --# Weight for nodes with obstacles --HighWeight = 3 -+# When movement flags of target node is blocked to any direction, multiply movement weight by this multiplier. -+# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 10 -+ObstacleMultiplier = 10 - --# Weight for diagonal movement. --# Default: LowWeight * sqrt(2) --DiagonalWeight = 0.707 -+# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 20 -+# For proper function must be higher than BaseWeight and/or DiagonalWeight. -+HeuristicWeight = 20 - -+# Maximum number of generated nodes per one path-finding process, default 3500 -+MaxIterations = 3500 -+ - # ================================================================= - # Other - # ================================================================= -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (working copy) -@@ -55,9 +55,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -@@ -73,9 +72,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (working copy) -@@ -18,11 +18,11 @@ - - import java.util.List; - --import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -+import org.l2jmobius.gameserver.geoengine.GeoEngine; - import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -+import org.l2jmobius.gameserver.network.SystemMessageId; - import org.l2jmobius.gameserver.util.BuilderUtil; - - public class AdminPathNode implements IAdminCommandHandler -@@ -37,29 +37,30 @@ - { - if (command.equals("admin_path_find")) - { -- if (!Config.PATHFINDING) -- { -- BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); -- return true; -- } - if (activeChar.getTarget() != null) - { -- final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); -+ final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); - if (path == null) - { -- BuilderUtil.sendSysMessage(activeChar, "No Route!"); -- return true; -+ BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); - } -- for (AbstractNodeLoc a : path) -+ else - { -- BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); -+ for (Location point : path) -+ { -+ BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); -+ } - } - } - else - { -- BuilderUtil.sendSysMessage(activeChar, "No Target!"); -+ activeChar.sendPacket(SystemMessageId.INVALID_TARGET); - } - } -+ else -+ { -+ return false; -+ } - return true; - } - -Index: java/org/l2jmobius/Config.java -=================================================================== ---- java/org/l2jmobius/Config.java (revision 8409) -+++ java/org/l2jmobius/Config.java (working copy) -@@ -32,7 +32,6 @@ - import java.net.UnknownHostException; - import java.nio.charset.StandardCharsets; - import java.nio.file.Files; --import java.nio.file.Path; - import java.nio.file.Paths; - import java.time.Duration; - import java.util.ArrayList; -@@ -966,14 +965,17 @@ - // -------------------------------------------------- - // GeoEngine - // -------------------------------------------------- -- public static Path GEODATA_PATH; -+ public static String GEODATA_PATH; -+ public static int COORD_SYNCHRONIZE; -+ public static int PART_OF_CHARACTER_HEIGHT; -+ public static int MAX_OBSTACLE_HEIGHT; - public static boolean PATHFINDING; - public static String PATHFIND_BUFFERS; -- public static float LOW_WEIGHT; -- public static float MEDIUM_WEIGHT; -- public static float HIGH_WEIGHT; -- public static float DIAGONAL_WEIGHT; -- public static int COORD_SYNCHRONIZE; -+ public static int BASE_WEIGHT; -+ public static int DIAGONAL_WEIGHT; -+ public static int HEURISTIC_WEIGHT; -+ public static int OBSTACLE_MULTIPLIER; -+ public static int MAX_ITERATIONS; - public static boolean CORRECT_PLAYER_Z; - - /** Attribute System */ -@@ -2568,14 +2570,17 @@ - - // Load GeoEngine config file (if exists) - final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); -- GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); -+ GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); -+ COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); -+ MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); - PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -- LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); -- MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); -- HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); -- DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); -- COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); -+ DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); -+ OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); -+ HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); -+ MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); - CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); - - // Load AllowedPlayerRaces config file (if exists) -Index: java/org/l2jmobius/gameserver/geoengine/GeoEngine.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (revision 8435) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (working copy) -@@ -16,100 +16,90 @@ - */ - package org.l2jmobius.gameserver.geoengine; - --import java.io.IOException; -+import java.io.File; - import java.io.RandomAccessFile; - import java.nio.ByteOrder; --import java.nio.channels.FileChannel.MapMode; --import java.nio.file.Files; --import java.nio.file.Path; --import java.util.concurrent.atomic.AtomicReferenceArray; --import java.util.logging.Level; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.List; - import java.util.logging.Logger; - - import org.l2jmobius.Config; - import org.l2jmobius.gameserver.data.xml.DoorData; - import org.l2jmobius.gameserver.data.xml.FenceData; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; --import org.l2jmobius.gameserver.geoengine.geodata.IRegion; --import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; --import org.l2jmobius.gameserver.geoengine.geodata.Region; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.World; - import org.l2jmobius.gameserver.model.WorldObject; -+import org.l2jmobius.gameserver.model.actor.Creature; - import org.l2jmobius.gameserver.model.instancezone.Instance; --import org.l2jmobius.gameserver.model.interfaces.ILocational; --import org.l2jmobius.gameserver.util.GeoUtils; --import org.l2jmobius.gameserver.util.LinePointIterator; --import org.l2jmobius.gameserver.util.LinePointIterator3D; -+import org.l2jmobius.gameserver.util.MathUtil; - - /** -- * @author -Nemesiss-, HorridoJoho -+ * @author Hasha - */ - public class GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); -+ protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - -- private static final int WORLD_MIN_X = -655360; -- private static final int WORLD_MIN_Y = -589824; -- private static final int WORLD_MIN_Z = -16384; -+ private final ABlock[][] _blocks; -+ private final BlockNull _nullBlock; - -- /** 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; -- -- /** The regions array */ -- private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); -- -- private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; -- private static final int ELEVATED_SEE_OVER_DISTANCE = 2; -- private static final int MAX_SEE_OVER_HEIGHT = 48; -- -- protected GeoEngine() -+ /** -+ * GeoEngine constructor. Loads all geodata files of chosen geodata format. -+ */ -+ public GeoEngine() - { - LOGGER.info("GeoEngine: Initializing..."); -- for (int i = 0; i < _regions.length(); i++) -- { -- _regions.set(i, NullRegion.INSTANCE); -- } - -+ // initialize block container -+ _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; -+ -+ // load null block -+ _nullBlock = new BlockNull(); -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files according to geoengine config setup - int loaded = 0; -- try -+ long fileSize = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { -- for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { -- for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) -+ final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); -+ if (f.exists() && !f.isDirectory()) - { -- final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); -- if (Files.exists(geoFilePath)) -+ // region file is load-able, try to load it -+ if (loadGeoBlocks(rx, ry)) - { -- try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) -- { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -- loaded++; -- } -+ loaded++; -+ fileSize += f.length(); - } - } -+ else -+ { -+ // region file is not load-able, load null blocks -+ loadNullBlocks(rx, ry); -+ } - } - } -- catch (Exception e) -+ -+ LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -+ if (loaded > 0) - { -- LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); -- System.exit(1); -+ LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); - } - -- LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -- -- // Avoid wrong configs when no files are loaded. -+ // avoid wrong configs when no files are loaded - if (loaded == 0) - { - if (Config.PATHFINDING) -@@ -123,619 +113,820 @@ - LOGGER.info("GeoEngine: Forcing CoordSynchronize setting to -1."); - } - } -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); - } - - /** -- * @param geoX -- * @param geoY -- * @return the region -+ * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. -+ * @return boolean : True, when geodata file was loaded without problem. - */ -- private IRegion getRegion(int geoX, int geoY) -+ private final boolean loadGeoBlocks(int regionX, int regionY) - { -- final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); -- if ((region < 0) || (region >= _regions.length())) -+ final String filename = String.format(GeoFormat.L2D.getFilename(), regionX, regionY); -+ -+ // standard load -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) - { -- return null; -+ // initialize file buffer -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, GeoFormat.L2D); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ } -+ -+ // check data consistency -+ if (buffer.remaining() > 0) -+ { -+ LOGGER.warning("GeoEngine: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ } -+ -+ // loading was successful -+ return true; - } -- return _regions.get(region); -- } -- -- /** -- * @param filePath -- * @param regionX -- * @param regionY -- * @throws IOException -- */ -- 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")) -+ catch (Exception e) - { -- _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -+ // an error occured while loading, load null blocks -+ LOGGER.warning("GeoEngine: Error while loading " + filename + " region file."); -+ LOGGER.warning(e.getMessage()); -+ -+ // replace whole region file with null blocks -+ loadNullBlocks(regionX, regionY); -+ -+ // loading was not successful -+ return false; - } - } - - /** -- * @param regionX -- * @param regionY -+ * Loads null blocks. Used when no region file is detected or an error occurs during loading. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. - */ -- public void unloadRegion(int regionX, int regionY) -+ private final void loadNullBlocks(int regionX, int regionY) - { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); -- } -- -- /** -- * @param geoX -- * @param geoY -- * @return if geodata exist -- */ -- public boolean hasGeoPos(int geoX, int geoY) -- { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // load all null blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { -- return false; -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[blockX + ix][blockY + iy] = _nullBlock; -+ } - } -- return region.hasGeo(); - } - -+ // GEODATA - GENERAL -+ - /** -- * Checks the specified position for available geodata. -- * @param x the world x -- * @param y the world y -- * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise -+ * Converts world X to geodata X. -+ * @param worldX -+ * @return int : Geo X - */ -- public boolean hasGeo(int x, int y) -+ public static int getGeoX(int worldX) - { -- return hasGeoPos(getGeoX(x), getGeoY(y)); -+ return (MathUtil.limit(worldX, World.MAP_MIN_X, World.MAP_MAX_X) - World.MAP_MIN_X) >> 4; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe check -+ * Converts world Y to geodata Y. -+ * @param worldY -+ * @return int : Geo Y - */ -- public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) -+ public static int getGeoY(int worldY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return true; -- } -- return region.checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(worldY, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.MAP_MIN_Y) >> 4; - } - - /** -+ * Converts geodata X to world X. - * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe anti-corner cut -+ * @return int : World X - */ -- public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) -+ public static int getWorldX(int geoX) - { -- boolean can = true; -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- return can && checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(geoX, 0, GeoStructure.GEO_CELLS_X) << 4) + World.MAP_MIN_X + 8; - } - - /** -- * @param geoX -+ * Converts geodata Y to world Y. - * @param geoY -- * @param worldZ -- * @return the nearest Z value -+ * @return int : World Y - */ -- public int getNearestZ(int geoX, int geoY, int worldZ) -+ public static int getWorldY(int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return worldZ; -- } -- return region.getNearestZ(geoX, geoY, worldZ); -+ return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next lower Z value -+ * Returns block of geodata on given coordinates. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return {@link ABlock} : Block of geodata. - */ -- public int getNextLowerZ(int geoX, int geoY, int worldZ) -+ private final ABlock getBlock(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final int x = geoX / GeoStructure.BLOCK_CELLS_X; -+ final int y = geoY / GeoStructure.BLOCK_CELLS_Y; -+ -+ // if x or y is out of array return null -+ if ((x > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) - { -- return worldZ; -+ return _blocks[x][y]; - } -- return region.getNextLowerZ(geoX, geoY, worldZ); -+ return null; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next higher Z value -+ * Check if geo coordinates has geo. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return boolean : True, if given geo coordinates have geodata - */ -- public int getNextHigherZ(int geoX, int geoY, int worldZ) -+ public boolean hasGeoPos(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final ABlock block = getBlock(geoX, geoY); -+ if (block == null) // null block check - { -- return worldZ; -+ // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) -+ // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); -+ return false; - } -- return region.getNextHigherZ(geoX, geoY, worldZ); -+ return block.hasGeoPos(); - } - - /** -- * Gets the Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the height of cell, which is closest to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, closest to given coordinates. - */ -- public int getHeight(int x, int y, int z) -+ public short getHeightNearest(int geoX, int geoY, int worldZ) - { -- return getNearestZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getHeightNearest(geoX, geoY, worldZ) : (short) worldZ; - } - - /** -- * Gets the next lower Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the NSWE flag byte of cell, which is closes to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. - */ -- public int getLowerHeight(int x, int y, int z) -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) - { -- return getNextLowerZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getNsweNearest(geoX, geoY, worldZ) : (byte) 0xFF; - } - - /** -- * Gets the next higher Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Check if world coordinates has geo. -+ * @param worldX : World X -+ * @param worldY : World Y -+ * @return boolean : True, if given world coordinates have geodata - */ -- public int getHigherHeight(int x, int y, int z) -+ public boolean hasGeo(int worldX, int worldY) - { -- return getNextHigherZ(getGeoX(x), getGeoY(y), z); -+ return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); - } - - /** -- * @param worldX -- * @return the geo X -+ * Returns closest Z coordinate according to geodata. -+ * @param worldX : world x -+ * @param worldY : world y -+ * @param worldZ : world z -+ * @return short : nearest Z coordinates according to geodata - */ -- public int getGeoX(int worldX) -+ public short getHeight(int worldX, int worldY, int worldZ) - { -- return (worldX - WORLD_MIN_X) / 16; -+ return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - -- /** -- * @param worldY -- * @return the geo Y -- */ -- public int getGeoY(int worldY) -- { -- return (worldY - WORLD_MIN_Y) / 16; -- } -+ // PATHFINDING - - /** -- * @param worldZ -- * @return the geo Z -+ * Check line of sight from {@link WorldObject} to {@link WorldObject}. -+ * @param origin : The origin object. -+ * @param target : The target object. -+ * @return {@code boolean} : True if origin can see target - */ -- public int getGeoZ(int worldZ) -+ public boolean canSeeTarget(WorldObject origin, WorldObject target) - { -- return (worldZ - WORLD_MIN_Z) / 16; -- } -- -- /** -- * @param geoX -- * @return the world X -- */ -- public int getWorldX(int geoX) -- { -- return (geoX * 16) + WORLD_MIN_X + 8; -- } -- -- /** -- * @param geoY -- * @return the world Y -- */ -- public int getWorldY(int geoY) -- { -- return (geoY * 16) + WORLD_MIN_Y + 8; -- } -- -- /** -- * @param geoZ -- * @return the world Z -- */ -- public int getWorldZ(int geoZ) -- { -- return (geoZ * 16) + WORLD_MIN_Z + 8; -- } -- -- /** -- * Can see target. Doors as target always return true. Checks doors between. -- * @param cha the character -- * @param target the target -- * @return {@code true} if the character can see the target (LOS), {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, WorldObject target) -- { -- if (target.isDoor()) -+ if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) - { -- // Can always see doors. - return true; - } -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param cha the character -- * @param worldPosition the world position -- * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) -- { -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param world -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @param tworld the target's instanceId -- * @return -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) -- { -- return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instance -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) -- { -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) -+ -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = target.getX(); -+ final int ty = target.getY(); -+ final int tz = target.getZ(); -+ -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } -- return canSeeTarget(x, y, z, tx, ty, tz); -- } -- -- /** -- * @param prevX -- * @param prevY -- * @param prevGeoZ -- * @param curX -- * @param curY -- * @param nswe -- * @return the LOS Z value -- */ -- private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) -- { -- if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { -- throw new RuntimeException("Multiple directions!"); -+ return false; - } - -- if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- return getNearestZ(curX, curY, prevGeoZ); -+ return true; - } - -- return getNextHigherZ(curX, curY, prevGeoZ); -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) -+ { -+ return true; -+ } -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) -+ { -+ return goz == gtz; -+ } -+ -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) -+ { -+ oheight = ((Creature) origin).getCollisionHeight() * 2; -+ } -+ -+ double theight = 0; -+ if (target.isCreature()) -+ { -+ theight = ((Creature) target).getCollisionHeight() * 2; -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); - } - - /** -- * Can see target. Does not check doors between. -- * @param xValue the x coordinate -- * @param yValue the y coordinate -- * @param zValue the z coordinate -- * @param txValue the target's x coordinate -- * @param tyValue the target's y coordinate -- * @param tzValue the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -+ * Check line of sight from {@link WorldObject} to {@link Location}. -+ * @param origin : The origin object. -+ * @param position : The target position. -+ * @return {@code boolean} : True if object can see position - */ -- public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) -+ public boolean canSeeTarget(WorldObject origin, Location position) - { -- int x = xValue; -- int y = yValue; -- int tx = txValue; -- int ty = tyValue; -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = position.getX(); -+ final int ty = position.getY(); -+ final int tz = position.getZ(); - -- int geoX = getGeoX(x); -- int geoY = getGeoY(y); -- int tGeoX = getGeoX(tx); -- int tGeoY = getGeoY(ty); -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) -+ { -+ return false; -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) -+ { -+ return false; -+ } - -- int z = getNearestZ(geoX, geoY, zValue); -- int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- // Fastpath. -- if ((geoX == tGeoX) && (geoY == tGeoY)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- if (hasGeoPos(tGeoX, tGeoY)) -- { -- return z == tz; -- } - return true; - } - -- if (tz > z) -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) - { -- int tmp = tx; -- tx = x; -- x = tmp; -- -- tmp = ty; -- ty = y; -- y = tmp; -- -- tmp = tz; -- tz = z; -- z = tmp; -- -- tmp = tGeoX; -- tGeoX = geoX; -- geoX = tmp; -- -- tmp = tGeoY; -- tGeoY = geoY; -- geoY = tmp; -+ return goz == gtz; - } - -- final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); -- // First point is guaranteed to be available, skip it, we can always see our own position. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- final int prevZ = pointIter.z(); -- int prevGeoZ = prevZ; -- int ptIndex = 0; -- while (pointIter.next()) -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight(); -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); -+ } -+ -+ /** -+ * Simple check for origin to target visibility. -+ * @param goxValue : origin X geodata coordinate -+ * @param goyValue : origin Y geodata coordinate -+ * @param gozValue : origin Z geodata coordinate -+ * @param oheight : origin height (if instance of {@link Character}) -+ * @param gtxValue : target X geodata coordinate -+ * @param gtyValue : target Y geodata coordinate -+ * @param gtzValue : target Z geodata coordinate -+ * @param theight : target height (if instance of {@link Character}) -+ * @param instance -+ * @return {@code boolean} : True, when target can be seen. -+ */ -+ private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) -+ { -+ int goz = gozValue; -+ int gtz = gtzValue; -+ int gox = goxValue; -+ int goy = goyValue; -+ int gtx = gtxValue; -+ int gty = gtyValue; -+ -+ // get line of sight Z coordinates -+ double losoz = goz + ((oheight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ double lostz = gtz + ((theight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ -+ // get X delta and signum -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirox = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; -+ final byte dirtx = sx > 0 ? GeoStructure.CELL_FLAG_W : GeoStructure.CELL_FLAG_E; -+ -+ // get Y delta and signum -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte diroy = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ final byte dirty = sy > 0 ? GeoStructure.CELL_FLAG_N : GeoStructure.CELL_FLAG_S; -+ -+ // get Z delta -+ final int dm = Math.max(dx, dy); -+ final double dz = (lostz - losoz) / dm; -+ -+ // get direction flag for diagonal movement -+ final byte diroxy = getDirXY(dirox, diroy); -+ final byte dirtxy = getDirXY(dirtx, dirty); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte diro; -+ byte dirt; -+ -+ // initialize node values -+ int nox = gox; -+ int noy = goy; -+ int ntx = gtx; -+ int nty = gty; -+ byte nsweo = getNsweNearest(gox, goy, goz); -+ byte nswet = getNsweNearest(gtx, gty, gtz); -+ -+ // loop -+ ABlock block; -+ int index; -+ for (int i = 0; i < ((dm + 1) / 2); i++) -+ { -+ // reset direction flag -+ diro = 0; -+ dirt = 0; - -- if ((curX == prevX) && (curY == prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- continue; -+ // calculate next point XY coordinates -+ d -= dy; -+ d += dx; -+ nox += sx; -+ ntx -= sx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroxy; -+ dirt |= dirtxy; - } -+ else if (e2 > -dy) -+ { -+ // calculate next point X coordinate -+ d -= dy; -+ nox += sx; -+ ntx -= sx; -+ diro |= dirox; -+ dirt |= dirtx; -+ } -+ else if (e2 < dx) -+ { -+ // calculate next point Y coordinate -+ d += dx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroy; -+ dirt |= dirty; -+ } - -- final int beeCurZ = pointIter.z(); -- int curGeoZ = prevGeoZ; -- -- // Check if the position has geodata. -- if (hasGeoPos(curX, curY)) - { -- final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); -- curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); -- int maxHeight; -- if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) -+ // get block of the next cell -+ block = getBlock(nox, noy); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nsweo & diro) == 0) - { -- maxHeight = z + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); - } - else - { -- maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); - } - -- boolean canSeeThrough = false; -- if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) -+ // layer does not exist, return -+ if (index == -1) - { -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else -- { -- canSeeThrough = true; -- } -+ return false; - } - -- if (!canSeeThrough) -+ // get layer and next line of sight Z coordinate -+ goz = block.getHeight(index); -+ losoz += dz; -+ -+ // perform line of sight check, return when fails -+ if ((goz - losoz) > Config.MAX_OBSTACLE_HEIGHT) - { - return false; - } -+ -+ // get layer nswe -+ nsweo = block.getNswe(index); - } -+ { -+ // get block of the next cell -+ block = getBlock(ntx, nty); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nswet & dirt) == 0) -+ { -+ index = block.getIndexAbove(ntx, nty, gtz - GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ else -+ { -+ index = block.getIndexBelow(ntx, nty, gtz + GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ -+ // layer does not exist, return -+ if (index == -1) -+ { -+ return false; -+ } -+ -+ // get layer and next line of sight Z coordinate -+ gtz = block.getHeight(index); -+ lostz -= dz; -+ -+ // perform line of sight check, return when fails -+ if ((gtz - lostz) > Config.MAX_OBSTACLE_HEIGHT) -+ { -+ return false; -+ } -+ -+ // get layer nswe -+ nswet = block.getNswe(index); -+ } - -- prevX = curX; -- prevY = curY; -- prevGeoZ = curGeoZ; -- ++ptIndex; -+ // update coords -+ gox = nox; -+ goy = noy; -+ gtx = ntx; -+ gty = nty; - } - -- return true; -+ // when iteration is completed, compare final Z coordinates -+ return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); - } - - /** -- * Move check. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param zValue the z coordinate -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tzValue the target's z coordinate -- * @param instance the instance -- * @return the last Location (x,y,z) where player can walk - just before wall -+ * Check movement from coordinates to coordinates. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {code boolean} : True if target coordinates are reachable from origin coordinates - */ -- public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) -+ public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- final int geoX = getGeoX(x); -- final int geoY = getGeoY(y); -- final int z = getNearestZ(geoX, geoY, zValue); -- final int tGeoX = getGeoX(tx); -- final int tGeoY = getGeoY(ty); -- final int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } -- if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = z; -+ // perform geodata check -+ final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); -+ return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); -+ } -+ -+ /** -+ * Check movement from origin to target. Returns last available point in the checked path. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {@link Location} : Last point where object can walk (just before wall) -+ */ -+ public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) -+ { -+ return new Location(ox, oy, oz); -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) -+ { -+ return new Location(ox, oy, oz); -+ } - -- while (pointIter.next()) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -- -- if (hasGeoPos(prevX, prevY)) -- { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -- { -- // Can't move, return previous location. -- return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); -- } -- } -- -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return new Location(tx, ty, tz); - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != tz)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- // Different floors, return start location. -- return new Location(x, y, z); -+ return new Location(tx, ty, tz); - } - -- return new Location(tx, ty, tz); -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) -+ { -+ return new Location(tx, ty, tz); -+ } -+ -+ // perform geodata check -+ return checkMove(gox, goy, goz, gtx, gty, gtz, instance); - } - - /** -- * Checks if its possible to move from one location to another. -- * @param fromX the X coordinate to start checking from -- * @param fromY the Y coordinate to start checking from -- * @param fromZvalue the Z coordinate to start checking from -- * @param toX the X coordinate to end checking at -- * @param toY the Y coordinate to end checking at -- * @param toZvalue the Z coordinate to end checking at -- * @param instance the instance -- * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise -+ * With this method you can check if a position is visible or can be reached by beeline movement.
-+ * Target X and Y reachable and Z is on same floor: -+ *
    -+ *
  • Location of the target with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y reachable but Z is on another floor: -+ *
    -+ *
  • Location of the origin with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y not reachable: -+ *
    -+ *
  • Last accessible location in destination to target.
  • -+ *
-+ * @param gox : origin X geodata coordinate -+ * @param goy : origin Y geodata coordinate -+ * @param goz : origin Z geodata coordinate -+ * @param gtx : target X geodata coordinate -+ * @param gty : target Y geodata coordinate -+ * @param gtz : target Z geodata coordinate -+ * @param instance -+ * @return {@link GeoLocation} : The last allowed point of movement. - */ -- public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) -+ protected final GeoLocation checkMove(int gox, int goy, int goz, int gtx, int gty, int gtz, Instance instance) - { -- final int geoX = getGeoX(fromX); -- final int geoY = getGeoY(fromY); -- final int fromZ = getNearestZ(geoX, geoY, fromZvalue); -- final int tGeoX = getGeoX(toX); -- final int tGeoY = getGeoY(toY); -- final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); -- -- if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) -+ if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } -- if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) -+ if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = fromZ; -+ // get X delta, signum and direction flag -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirX = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; - -- while (pointIter.next()) -+ // get Y delta, signum and direction flag -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte dirY = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ -+ // get direction flag for diagonal movement -+ final byte dirXY = getDirXY(dirX, dirY); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte direction; -+ -+ // load pointer coordinates -+ int gpx = gox; -+ int gpy = goy; -+ int gpz = goz; -+ -+ // load next pointer -+ int nx = gpx; -+ int ny = gpy; -+ -+ // loop -+ int count = 0; -+ while (count++ < Config.MAX_ITERATIONS) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -+ direction = 0; - -- if (hasGeoPos(prevX, prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -+ d -= dy; -+ d += dx; -+ nx += sx; -+ ny += sy; -+ direction |= dirXY; -+ } -+ else if (e2 > -dy) -+ { -+ d -= dy; -+ nx += sx; -+ direction |= dirX; -+ } -+ else if (e2 < dx) -+ { -+ d += dx; -+ ny += sy; -+ direction |= dirY; -+ } -+ -+ // obstacle found, return -+ if ((getNsweNearest(gpx, gpy, gpz) & direction) == 0) -+ { -+ return new GeoLocation(gpx, gpy, gpz); -+ } -+ -+ // update pointer coordinates -+ gpx = nx; -+ gpy = ny; -+ gpz = getHeightNearest(nx, ny, gpz); -+ -+ // target coordinates reached -+ if ((gpx == gtx) && (gpy == gty)) -+ { -+ if (gpz == gtz) - { -- return false; -+ // path found, Z coordinates are okay, return target point -+ return new GeoLocation(gtx, gty, gtz); - } -+ -+ // path found, Z coordinates are not okay, return last good point -+ return new GeoLocation(gpx, gpy, gpz); - } -+ } -+ -+ return new GeoLocation(gox, goy, goz); -+ } -+ -+ /** -+ * Returns diagonal NSWE flag format of combined two NSWE flags. -+ * @param dirX : X direction NSWE flag -+ * @param dirY : Y direction NSWE flag -+ * @return byte : NSWE flag of combined direction -+ */ -+ private static byte getDirXY(byte dirX, byte dirY) -+ { -+ // check axis directions -+ if (dirY == GeoStructure.CELL_FLAG_N) -+ { -+ if (dirX == GeoStructure.CELL_FLAG_W) -+ { -+ return GeoStructure.CELL_FLAG_NW; -+ } - -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return GeoStructure.CELL_FLAG_NE; - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) -+ if (dirX == GeoStructure.CELL_FLAG_W) - { -- // Different floors. -- return false; -+ return GeoStructure.CELL_FLAG_SW; - } - -- return true; -+ return GeoStructure.CELL_FLAG_SE; - } - -+ /** -+ * Returns the list of location objects as a result of complete path calculation. -+ * @param ox : origin x -+ * @param oy : origin y -+ * @param oz : origin z -+ * @param tx : target x -+ * @param ty : target y -+ * @param tz : target z -+ * @param instance -+ * @return {@code List} : complete path from nodes -+ */ -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ return null; -+ } -+ -+ /** -+ * Returns the instance of the {@link GeoEngine}. -+ * @return {@link GeoEngine} : The instance. -+ */ - public static GeoEngine getInstance() - { - return SingletonHolder.INSTANCE; -@@ -743,6 +934,6 @@ - - private static class SingletonHolder - { -- protected static final GeoEngine INSTANCE = new GeoEngine(); -+ protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); - } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (working copy) -@@ -20,88 +20,84 @@ - import java.util.LinkedList; - import java.util.List; - import java.util.ListIterator; --import java.util.logging.Level; --import java.util.logging.Logger; - - import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; --import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; --import org.l2jmobius.gameserver.model.World; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -+import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.instancezone.Instance; - - /** -- * @author -Nemesiss- -+ * @author Hasha - */ --public class GeoEnginePathfinding -+final class GeoEnginePathfinding extends GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); -+ // pre-allocated buffers -+ private final BufferHolder[] _buffers; - -- private BufferInfo[] _buffers; -- - protected GeoEnginePathfinding() - { -- try -+ super(); -+ -+ final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ _buffers = new BufferHolder[array.length]; -+ int count = 0; -+ for (int i = 0; i < array.length; i++) - { -- final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ final String buf = array[i]; -+ final String[] args = buf.split("x"); - -- _buffers = new BufferInfo[array.length]; -- -- String buf; -- String[] args; -- for (int i = 0; i < array.length; i++) -+ try - { -- buf = array[i]; -- args = buf.split("x"); -- if (args.length != 2) -- { -- throw new Exception("Invalid buffer definition: " + buf); -- } -- -- _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); -+ final int size = Integer.parseInt(args[1]); -+ count += size; -+ _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); - } -+ catch (Exception e) -+ { -+ LOGGER.warning("GeoEnginePathfinding: Can not load buffer setting: " + buf); -+ } - } -- catch (Exception e) -- { -- LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); -- throw new Error("CellPathFinding: load aborted"); -- } -+ -+ LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); - } - -- public boolean pathNodesExist(short regionoffset) -+ @Override -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- return false; -- } -- -- public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) -- { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -- if (!GeoEngine.getInstance().hasGeo(x, y)) -+ // get origin and check existing geo coords -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { - 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)) -+ -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coords -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { - 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)))); -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // Prepare buffer for pathfinding calculations -+ final NodeBuffer buffer = getBuffer(64 + (2 * Math.max(Math.abs(gox - gtx), Math.abs(goy - gty)))); - if (buffer == null) - { - return null; - } - -- List path = null; -+ // find path -+ List path = null; - try - { -- final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); -- -+ final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); - if (result == null) - { - return null; -@@ -125,33 +121,45 @@ - return path; - } - -- int currentX, currentY, currentZ; -- ListIterator middlePoint; -+ // get path list iterator -+ final ListIterator point = path.listIterator(); - -- middlePoint = path.listIterator(); -- currentX = x; -- currentY = y; -- currentZ = z; -+ // get node A (origin) -+ int nodeAx = gox; -+ int nodeAy = goy; -+ short nodeAz = goz; - -- while (middlePoint.hasNext()) -+ // get node B -+ GeoLocation nodeB = (GeoLocation) point.next(); -+ -+ // iterate thought the path to optimize it -+ int count = 0; -+ while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) - { -- final AbstractNodeLoc locMiddle = middlePoint.next(); -- if (!middlePoint.hasNext()) -- { -- break; -- } -+ // get node C -+ final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - -- final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); -- if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) -+ // check movement from node A to node C -+ final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); -+ if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) - { -- middlePoint.remove(); -+ // can move from node A to node C -+ -+ // remove node B -+ point.remove(); - } - else - { -- currentX = locMiddle.getX(); -- currentY = locMiddle.getY(); -- currentZ = locMiddle.getZ(); -+ // can not move from node A to node C -+ -+ // set node A (node B is part of path, update A coordinates) -+ nodeAx = nodeB.getGeoX(); -+ nodeAy = nodeB.getGeoY(); -+ nodeAz = (short) nodeB.getZ(); - } -+ -+ // set node B -+ nodeB = (GeoLocation) point.next(); - } - - return path; -@@ -158,144 +166,102 @@ - } - - /** -- * Convert geodata position to pathnode position -- * @param geo_pos -- * @return pathnode position -+ * Create list of node locations as result of calculated buffer node tree. -+ * @param node : the entry point -+ * @return List : list of node location - */ -- public short getNodePos(int geo_pos) -+ private static List constructPath(Node node) - { -- return (short) (geo_pos >> 3); // OK? -- } -- -- /** -- * Convert node position to pathnode block position -- * @param node_pos -- * @return pathnode block position (0...255) -- */ -- public short getNodeBlock(int node_pos) -- { -- return (short) (node_pos % 256); -- } -- -- public byte getRegionX(int node_pos) -- { -- return (byte) ((node_pos >> 8) + World.TILE_X_MIN); -- } -- -- public byte getRegionY(int node_pos) -- { -- return (byte) ((node_pos >> 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 node_x rx -- * @return -- */ -- public int calculateWorldX(short node_x) -- { -- return World.MAP_MIN_X + (node_x * 128) + 48; -- } -- -- /** -- * Convert pathnode y to World y position -- * @param node_y -- * @return -- */ -- public int calculateWorldY(short node_y) -- { -- return World.MAP_MIN_Y + (node_y * 128) + 48; -- } -- -- private List constructPath(AbstractNode nodeValue) -- { -- final LinkedList path = new LinkedList<>(); -- int previousDirectionX = Integer.MIN_VALUE; -- int previousDirectionY = Integer.MIN_VALUE; -- int directionX, directionY; -+ // create empty list -+ final LinkedList list = new LinkedList<>(); - -- AbstractNode node = nodeValue; -- while (node.getParent() != null) -+ // set direction X/Y -+ int dx = 0; -+ int dy = 0; -+ -+ // get target parent -+ Node target = node; -+ Node parent = target.getParent(); -+ -+ // while parent exists -+ int count = 0; -+ while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { -- directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); -- directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); -+ // get parent <> target direction X/Y -+ final int nx = parent.getLoc().getGeoX() - target.getLoc().getGeoX(); -+ final int ny = parent.getLoc().getGeoY() - target.getLoc().getGeoY(); - -- // only add a new route point if moving direction changes -- if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) -+ // direction has changed? -+ if ((dx != nx) || (dy != ny)) - { -- previousDirectionX = directionX; -- previousDirectionY = directionY; -+ // add node to the beginning of the list -+ list.addFirst(target.getLoc()); - -- path.addFirst(node.getLoc()); -- node.setLoc(null); -+ // update direction X/Y -+ dx = nx; -+ dy = ny; - } - -- node = node.getParent(); -+ // move to next node, set target and get its parent -+ target = parent; -+ parent = target.getParent(); - } - -- return path; -+ // return list -+ return list; - } - -- private CellNodeBuffer alloc(int size) -+ /** -+ * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer. -+ * @param size : pre-calculated minimal required size -+ * @return NodeBuffer : buffer -+ */ -+ private final NodeBuffer getBuffer(int size) - { -- CellNodeBuffer current = null; -- for (BufferInfo i : _buffers) -+ NodeBuffer current = null; -+ for (BufferHolder holder : _buffers) - { -- if (i.mapSize >= size) -+ // Find proper size of buffer -+ if (holder._size < size) - { -- for (CellNodeBuffer buf : i.buffer) -+ continue; -+ } -+ -+ // Find unlocked NodeBuffer -+ for (NodeBuffer buffer : holder._buffer) -+ { -+ if (!buffer.isLocked()) - { -- if (buf.lock()) -- { -- current = buf; -- break; -- } -+ continue; - } -- if (current != null) -- { -- break; -- } - -- // not found, allocate temporary buffer -- current = new CellNodeBuffer(i.mapSize); -- current.lock(); -- if (i.buffer.size() < i.count) -- { -- i.buffer.add(current); -- break; -- } -+ return buffer; - } -+ -+ // NodeBuffer not found, allocate temporary buffer -+ current = new NodeBuffer(holder._size); -+ current.isLocked(); - } - - return current; - } - -- private static final class BufferInfo -+ /** -+ * NodeBuffer container with specified size and count of separate buffers. -+ */ -+ private static final class BufferHolder - { -- final int mapSize; -- final int count; -- ArrayList buffer; -+ final int _size; -+ List _buffer; - -- public BufferInfo(int size, int cnt) -+ public BufferHolder(int size, int count) - { -- mapSize = size; -- count = cnt; -- buffer = new ArrayList<>(count); -+ _size = size; -+ _buffer = new ArrayList<>(count); -+ for (int i = 0; i < count; i++) -+ { -+ _buffer.add(new NodeBuffer(size)); -+ } - } - } -- -- public static GeoEnginePathfinding getInstance() -- { -- return SingletonHolder.INSTANCE; -- } -- -- private static class SingletonHolder -- { -- protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); -- } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (working copy) -@@ -0,0 +1,197 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @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 height of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getHeightNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first above given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, above given coordinates. -+ */ -+ public abstract short getHeightAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first below given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is closest to given coordinates. -+ * @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 the NSWE flag byte of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getNsweNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first above given coordinates. -+ * @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 getNsweAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first below given coordinates. -+ * @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 getNsweBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is closes layer to given coordinates. -+ * @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. -+ * @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 above given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is first layer below given coordinates. -+ * @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 index to data of the cell, which is first layer below given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexBelowOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeight(int index); -+ -+ /** -+ * Returns the height of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightOriginal(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNswe(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNsweOriginal(int index); -+ -+ /** -+ * Sets the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @param nswe : New NSWE flag byte. -+ */ -+ public abstract void setNswe(int index, byte nswe); -+ -+ /** -+ * Saves the block in L2D format to {@link BufferedOutputStream}. Used only for L2D geodata conversion. -+ * @param stream : The stream. -+ * @throws IOException : Can't save the block to steam. -+ */ -+ public abstract void saveBlock(BufferedOutputStream stream) throws IOException; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (working copy) -@@ -0,0 +1,252 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockComplex(ByteBuffer bb, GeoFormat format) -+ { -+ // initialize buffer -+ _buffer = new byte[GeoStructure.BLOCK_CELLS * 3]; -+ -+ // load data -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // 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); -+ } -+ else -+ { -+ // get nswe -+ final byte nswe = bb.get(); -+ _buffer[i * 3] = nswe; -+ -+ // get height -+ final short height = bb.getShort(); -+ _buffer[(i * 3) + 1] = (byte) (height & 0x00FF); -+ _buffer[(i * 3) + 2] = (byte) (height >> 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height > worldZ ? height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height < worldZ ? height : Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public 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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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 int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_COMPLEX_L2D); -+ -+ // write block data -+ stream.write(_buffer, 0, GeoStructure.BLOCK_CELLS * 3); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (working copy) -@@ -0,0 +1,176 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockFlat extends ABlock -+{ -+ protected final short _height; -+ protected byte _nswe; -+ -+ /** -+ * Creates FlatBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockFlat(ByteBuffer bb, GeoFormat format) -+ { -+ _height = bb.getShort(); -+ _nswe = format != GeoFormat.L2D ? 0x0F : (byte) (0xFF); -+ if (format == GeoFormat.L2OFF) -+ { -+ bb.getShort(); -+ } -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return true; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height > worldZ ? _height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height < worldZ ? _height : Short.MAX_VALUE; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height > worldZ ? _nswe : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height < worldZ ? _nswe : 0; -+ } -+ -+ @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return index -+ return _height < worldZ ? 0 : -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _nswe = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_FLAT_L2D); -+ -+ // write height -+ stream.write((byte) (_height & 0x00FF)); -+ stream.write((byte) (_height >> 8)); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (working copy) -@@ -0,0 +1,465 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+import java.nio.ByteOrder; -+import java.util.Arrays; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockMultilayer extends ABlock -+{ -+ private static final int MAX_LAYERS = Byte.MAX_VALUE; -+ -+ private static ByteBuffer _temp; -+ -+ /** -+ * Initializes the temporarily buffer. -+ */ -+ public static void initialize() -+ { -+ // initialize temporarily buffer and sorting mechanism -+ _temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3); -+ _temp.order(ByteOrder.LITTLE_ENDIAN); -+ } -+ -+ /** -+ * Releases temporarily buffer. -+ */ -+ public static void release() -+ { -+ _temp = null; -+ } -+ -+ protected byte[] _buffer; -+ -+ /** -+ * Implicit constructor for children class. -+ */ -+ protected BlockMultilayer() -+ { -+ _buffer = null; -+ } -+ -+ /** -+ * Creates MultilayerBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockMultilayer(ByteBuffer bb, GeoFormat format) -+ { -+ // 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 = format != GeoFormat.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++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // get data -+ final short data = bb.getShort(); -+ -+ // add nswe and height -+ _temp.put((byte) (data & 0x000F)); -+ _temp.putShort((short) ((short) (data & 0xFFF0) >> 1)); -+ } -+ else -+ { -+ // add nswe -+ _temp.put(bb.get()); -+ -+ // add height -+ _temp.putShort(bb.getShort()); -+ } -+ } -+ } -+ -+ // 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 height -+ if (height > worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, return minimum value -+ return Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 height -+ if (height < worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, return maximum value -+ return Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 nswe -+ if (height > worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 nswe -+ if (height < worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @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 first layer data (first from bottom) -+ byte layers = _buffer[index++]; -+ -+ // loop though all cell layers, find closest layer -+ 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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ // get height -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(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]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ // get nswe -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ // set nswe -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_MULTILAYER_L2D); -+ -+ // for each cell -+ int index = 0; -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ // write layers count -+ final byte layers = _buffer[index++]; -+ stream.write(layers); -+ -+ // write cell data -+ stream.write(_buffer, index, layers * 3); -+ -+ // move index to next cell -+ index += layers * 3; -+ } -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (working copy) -@@ -0,0 +1,150 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockNull extends ABlock -+{ -+ private final byte _nswe; -+ -+ public BlockNull() -+ { -+ _nswe = (byte) 0xFF; -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return false; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweBelow(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) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) -+ { -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (nonexistent) -@@ -1,48 +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 . -- */ --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() -- { -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (nonexistent) -@@ -1,77 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (nonexistent) -@@ -1,56 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (working copy) -@@ -0,0 +1,39 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public enum GeoFormat -+{ -+ L2J("%d_%d.l2j"), -+ L2OFF("%d_%d_conv.dat"), -+ L2D("%d_%d.l2d"); -+ -+ private final String _filename; -+ -+ private GeoFormat(String filename) -+ { -+ _filename = filename; -+ } -+ -+ public String getFilename() -+ { -+ return _filename; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (working copy) -@@ -0,0 +1,67 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.geoengine.GeoEngine; -+import org.l2jmobius.gameserver.model.Location; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoLocation extends Location -+{ -+ private byte _nswe; -+ -+ public GeoLocation(int x, int y, int z) -+ { -+ super(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public void set(int x, int y, short z) -+ { -+ super.setXYZ(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public int getGeoX() -+ { -+ return _x; -+ } -+ -+ public int getGeoY() -+ { -+ return _y; -+ } -+ -+ @Override -+ public int getX() -+ { -+ return GeoEngine.getWorldX(_x); -+ } -+ -+ @Override -+ public int getY() -+ { -+ return GeoEngine.getWorldY(_y); -+ } -+ -+ public byte getNSWE() -+ { -+ return _nswe; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (working copy) -@@ -0,0 +1,70 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoStructure -+{ -+ // cells -+ public static final byte CELL_FLAG_E = 1 << 0; -+ public static final byte CELL_FLAG_W = 1 << 1; -+ public static final byte CELL_FLAG_S = 1 << 2; -+ public static final byte CELL_FLAG_N = 1 << 3; -+ public static final byte CELL_FLAG_SE = 1 << 4; -+ public static final byte CELL_FLAG_SW = 1 << 5; -+ public static final byte CELL_FLAG_NE = 1 << 6; -+ public static final byte CELL_FLAG_NW = (byte) (1 << 7); -+ -+ public static final int CELL_HEIGHT = 8; -+ public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6; -+ -+ // blocks -+ public static final byte TYPE_FLAT_L2J_L2OFF = 0; -+ public static final byte TYPE_FLAT_L2D = (byte) 0xD0; -+ public static final byte TYPE_COMPLEX_L2J = 1; -+ public static final byte TYPE_COMPLEX_L2OFF = 0x40; -+ public static final byte TYPE_COMPLEX_L2D = (byte) 0xD1; -+ 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) -+ public static final byte TYPE_MULTILAYER_L2D = (byte) 0xD2; -+ -+ 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; -+ -+ // regions -+ 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; -+ -+ // global geodata -+ private static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); -+ private 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; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (nonexistent) -@@ -1,42 +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 . -- */ --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); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (working copy) -@@ -0,0 +1,53 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public interface IGeoObject -+{ -+ /** -+ * Returns geodata X coordinate of the {@link IGeoObject}. -+ * @return int : Geodata X coordinate. -+ */ -+ int getGeoX(); -+ -+ /** -+ * Returns geodata Y coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Y coordinate. -+ */ -+ int getGeoY(); -+ -+ /** -+ * Returns geodata Z coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Z coordinate. -+ */ -+ int getGeoZ(); -+ -+ /** -+ * Returns height of the {@link IGeoObject}. -+ * @return int : Height. -+ */ -+ int getHeight(); -+ -+ /** -+ * Returns {@link IGeoObject} data. -+ * @return byte[][] : {@link IGeoObject} data. -+ */ -+ byte[][] getObjectGeoData(); -+} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (nonexistent) -@@ -1,47 +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 . -- */ --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(); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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); -- -- // 1 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (nonexistent) -@@ -1,55 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --/** -- * @author HorridoJoho -- */ --public final class NullRegion implements IRegion --{ -- public static final NullRegion INSTANCE = new NullRegion(); -- -- @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 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Region.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (nonexistent) -@@ -1,92 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (nonexistent) -@@ -1,87 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public abstract class AbstractNode --{ -- private T _loc; -- private AbstractNode _parent; -- -- public AbstractNode(T loc) -- { -- _loc = loc; -- } -- -- public void setParent(AbstractNode p) -- { -- _parent = p; -- } -- -- public AbstractNode getParent() -- { -- return _parent; -- } -- -- public T getLoc() -- { -- return _loc; -- } -- -- public void setLoc(T l) -- { -- _loc = l; -- } -- -- @Override -- public int hashCode() -- { -- final int prime = 31; -- int result = 1; -- result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); -- return result; -- } -- -- @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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (nonexistent) -@@ -1,33 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --/** -- * @author -Nemesiss- -- */ --public abstract class AbstractNodeLoc --{ -- public abstract int getX(); -- -- public abstract int getY(); -- -- public abstract int getZ(); -- -- public abstract int getNodeX(); -- -- public abstract int getNodeY(); --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (nonexistent) -@@ -1,67 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public class CellNode extends AbstractNode --{ -- 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (nonexistent) -@@ -1,348 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --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 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) -- { -- _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(); -- } -- -- public final List debugPath() -- { -- final List 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 void getNeighbors() -- { -- if (!_current.getLoc().canGoAll()) -- { -- 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); -- } -- -- // SouthEast -- if ((nodeE != null) && (nodeS != null)) -- { -- if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) -- { -- addNode(x + 1, y + 1, z, true); -- } -- } -- -- // SouthWest -- if ((nodeS != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) -- { -- addNode(x - 1, y + 1, z, true); -- } -- } -- -- // NorthEast -- if ((nodeN != null) && (nodeE != null)) -- { -- if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) -- { -- addNode(x + 1, y - 1, z, true); -- } -- } -- -- // NorthWest -- if ((nodeN != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) -- { -- addNode(x - 1, y - 1, z, true); -- } -- } -- } -- -- private 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(); -- // reinit node if needed -- if (result.getLoc() != null) -- { -- result.getLoc().set(x, y, z); -- } -- else -- { -- result.setLoc(new NodeLoc(x, y, z)); -- } -- } -- -- return result; -- } -- -- private 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 boolean isHighWeight(int x, int y, int z) -- { -- final CellNode result = getNode(x, y, z); -- if (result == null) -- { -- return true; -- } -- -- if (!result.getLoc().canGoAll()) -- { -- return true; -- } -- if (Math.abs(result.getLoc().getZ() - z) > 16) -- { -- return true; -- } -- -- return false; -- } -- -- private 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (working copy) -@@ -0,0 +1,87 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+ -+/** -+ * @author Hasha -+ */ -+public class Node -+{ -+ // node coords and nswe flag -+ private GeoLocation _loc; -+ -+ // node parent (for reverse path construction) -+ private Node _parent; -+ // node child (for moving over nodes during iteration) -+ private Node _child; -+ -+ // node G cost (movement cost = parent movement cost + current movement cost) -+ private double _cost = -1000; -+ -+ public void setLoc(int x, int y, int z) -+ { -+ _loc = new GeoLocation(x, y, z); -+ } -+ -+ public GeoLocation getLoc() -+ { -+ return _loc; -+ } -+ -+ public void setParent(Node parent) -+ { -+ _parent = parent; -+ } -+ -+ public Node getParent() -+ { -+ return _parent; -+ } -+ -+ public void setChild(Node child) -+ { -+ _child = child; -+ } -+ -+ public Node getChild() -+ { -+ return _child; -+ } -+ -+ public void setCost(double cost) -+ { -+ _cost = cost; -+ } -+ -+ public double getCost() -+ { -+ return _cost; -+ } -+ -+ public void free() -+ { -+ // reset node location -+ _loc = null; -+ -+ // reset node parent, child and cost -+ _parent = null; -+ _child = null; -+ _cost = -1000; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (working copy) -@@ -0,0 +1,306 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import java.util.concurrent.locks.ReentrantLock; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+ -+/** -+ * @author DS, Hasha; Credits to Diamond -+ */ -+public class NodeBuffer -+{ -+ private final ReentrantLock _lock = new ReentrantLock(); -+ private final int _size; -+ private final Node[][] _buffer; -+ -+ // center coordinates -+ private int _cx = 0; -+ private int _cy = 0; -+ -+ // target coordinates -+ private int _gtx = 0; -+ private int _gty = 0; -+ private short _gtz = 0; -+ -+ private Node _current = null; -+ -+ /** -+ * Constructor of NodeBuffer. -+ * @param size : one dimension size of buffer -+ */ -+ public NodeBuffer(int size) -+ { -+ // set size -+ _size = size; -+ -+ // initialize buffer -+ _buffer = new Node[size][size]; -+ for (int x = 0; x < size; x++) -+ { -+ for (int y = 0; y < size; y++) -+ { -+ _buffer[x][y] = 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 Node : first node of path -+ */ -+ public Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) -+ { -+ // set coordinates (middle of the line (gox,goy) - (gtx,gty), will be in the center of the buffer) -+ _cx = gox + ((gtx - gox - _size) / 2); -+ _cy = goy + ((gty - goy - _size) / 2); -+ -+ _gtx = gtx; -+ _gty = gty; -+ _gtz = gtz; -+ -+ _current = getNode(gox, goy, goz); -+ _current.setCost(getCostH(gox, goy, goz)); -+ -+ int count = 0; -+ do -+ { -+ // reached target? -+ if ((_current.getLoc().getGeoX() == _gtx) && (_current.getLoc().getGeoY() == _gty) && (Math.abs(_current.getLoc().getZ() - _gtz) < 8)) -+ { -+ return _current; -+ } -+ -+ // expand current node -+ expand(); -+ -+ // move pointer -+ _current = _current.getChild(); -+ } -+ while ((_current != null) && (count++ < Config.MAX_ITERATIONS)); -+ -+ return null; -+ } -+ -+ public boolean isLocked() -+ { -+ return _lock.tryLock(); -+ } -+ -+ public void free() -+ { -+ _current = null; -+ -+ for (Node[] nodes : _buffer) -+ { -+ for (Node node : nodes) -+ { -+ if (node.getLoc() != null) -+ { -+ node.free(); -+ } -+ } -+ } -+ -+ _lock.unlock(); -+ } -+ -+ /** -+ * Check _current Node and add its neighbors to the buffer. -+ */ -+ private final void expand() -+ { -+ // can't move anywhere, don't expand -+ final byte nswe = _current.getLoc().getNSWE(); -+ if (nswe == 0) -+ { -+ return; -+ } -+ -+ // get geo coords of the node to be expanded -+ final int x = _current.getLoc().getGeoX(); -+ final int y = _current.getLoc().getGeoY(); -+ final short z = (short) _current.getLoc().getZ(); -+ -+ // can move north, expand -+ if ((nswe & GeoStructure.CELL_FLAG_N) != 0) -+ { -+ addNode(x, y - 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move south, expand -+ if ((nswe & GeoStructure.CELL_FLAG_S) != 0) -+ { -+ addNode(x, y + 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_W) != 0) -+ { -+ addNode(x - 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_E) != 0) -+ { -+ addNode(x + 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move north-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NW) != 0) -+ { -+ addNode(x - 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move north-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NE) != 0) -+ { -+ addNode(x + 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SW) != 0) -+ { -+ addNode(x - 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SE) != 0) -+ { -+ addNode(x + 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ } -+ -+ /** -+ * Returns node, if it exists in buffer. -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param z : node Z coord -+ * @return Node : node, if exits in buffer -+ */ -+ private final Node getNode(int x, int y, short z) -+ { -+ // check node X out of coordinates -+ final int ix = x - _cx; -+ if ((ix < 0) || (ix >= _size)) -+ { -+ return null; -+ } -+ -+ // check node Y out of coordinates -+ final int iy = y - _cy; -+ if ((iy < 0) || (iy >= _size)) -+ { -+ return null; -+ } -+ -+ // get node -+ final Node result = _buffer[ix][iy]; -+ -+ // check and update -+ if (result.getLoc() == null) -+ { -+ result.setLoc(x, y, z); -+ } -+ -+ // return node -+ return result; -+ } -+ -+ /** -+ * Add node given by coordinates to the buffer. -+ * @param x : geo X coord -+ * @param y : geo Y coord -+ * @param z : geo Z coord -+ * @param weight : weight of movement to new node -+ */ -+ private final void addNode(int x, int y, short z, int weight) -+ { -+ // get node to be expanded -+ final Node node = getNode(x, y, z); -+ if (node == null) -+ { -+ return; -+ } -+ -+ // Z distance between nearby cells is higher than cell size -+ if (node.getLoc().getZ() > (z + (2 * GeoStructure.CELL_HEIGHT))) -+ { -+ return; -+ } -+ -+ // node was already expanded, return -+ if (node.getCost() >= 0) -+ { -+ return; -+ } -+ -+ node.setParent(_current); -+ if (node.getLoc().getNSWE() != (byte) 0xFF) -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + (weight * Config.OBSTACLE_MULTIPLIER)); -+ } -+ else -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + weight); -+ } -+ -+ Node current = _current; -+ int count = 0; -+ while ((current.getChild() != null) && (count < (Config.MAX_ITERATIONS * 4))) -+ { -+ count++; -+ if (current.getChild().getCost() > node.getCost()) -+ { -+ node.setChild(current.getChild()); -+ break; -+ } -+ current = current.getChild(); -+ } -+ -+ if (count >= (Config.MAX_ITERATIONS * 4)) -+ { -+ System.err.println("Pathfinding: too long loop detected, cost:" + node.getCost()); -+ } -+ -+ current.setChild(node); -+ } -+ -+ /** -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param i : node Z coord -+ * @return double : node cost -+ */ -+ private final double getCostH(int x, int y, int i) -+ { -+ final int dX = x - _gtx; -+ final int dY = y - _gty; -+ final int dZ = (i - _gtz) / GeoStructure.CELL_HEIGHT; -+ -+ // return (Math.abs(dX) + Math.abs(dY) + Math.abs(dZ)) * Config.HEURISTIC_WEIGHT; // Manhattan distance -+ return Math.sqrt((dX * dX) + (dY * dY) + (dZ * dZ)) * Config.HEURISTIC_WEIGHT; // Direct distance -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -- --/** -- * @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 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 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; -- // return super.hashCode(); -- } -- -- @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; -- if (_x != other._x) -- { -- return false; -- } -- if (_y != other._y) -- { -- return false; -- } -- if (_goNorth != other._goNorth) -- { -- return false; -- } -- if (_goEast != other._goEast) -- { -- return false; -- } -- if (_goSouth != other._goSouth) -- { -- return false; -- } -- if (_goWest != other._goWest) -- { -- return false; -- } -- if (_geoHeight != other._geoHeight) -- { -- return false; -- } -- return true; -- } --} -Index: java/org/l2jmobius/gameserver/model/actor/Creature.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/Creature.java (revision 8428) -+++ java/org/l2jmobius/gameserver/model/actor/Creature.java (working copy) -@@ -65,8 +65,6 @@ - import org.l2jmobius.gameserver.enums.TeleportWhereType; - import org.l2jmobius.gameserver.enums.UserInfoType; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; - import org.l2jmobius.gameserver.instancemanager.IdManager; - import org.l2jmobius.gameserver.instancemanager.MapRegionManager; - import org.l2jmobius.gameserver.instancemanager.QuestManager; -@@ -2566,7 +2564,7 @@ - - public boolean disregardingGeodata; - public int onGeodataPathIndex; -- public List geoPath; -+ public List geoPath; - public int geoPathAccurateTx; - public int geoPathAccurateTy; - public int geoPathGtx; -@@ -3443,7 +3441,7 @@ - if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) - { - // Path calculation -- overrides previous movement check -- m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); -+ m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); - if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found - { - if (isPlayer() && !_isFlying && !isInWater) -Index: java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (revision 8424) -+++ java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (working copy) -@@ -12746,7 +12746,7 @@ - { - if (Config.CORRECT_PLAYER_Z) - { -- final int nearestZ = GeoEngine.getInstance().getHigherHeight(getX(), getY(), getZ()); -+ final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); - if (getZ() < nearestZ) - { - teleToLocation(new Location(getX(), getY(), nearestZ)); -Index: java/org/l2jmobius/gameserver/util/GeoUtils.java -=================================================================== ---- java/org/l2jmobius/gameserver/util/GeoUtils.java (revision 8409) -+++ java/org/l2jmobius/gameserver/util/GeoUtils.java (working copy) -@@ -19,7 +19,7 @@ - import java.awt.Color; - - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; - import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; - -@@ -26,25 +26,25 @@ - /** - * @author HorridoJoho - */ --public final class GeoUtils -+public class GeoUtils - { - public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); - - final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); - - while (iter.next()) - { -- final int wx = GeoEngine.getInstance().getWorldX(iter.x()); -- final int wy = GeoEngine.getInstance().getWorldY(iter.y()); -+ final int wx = GeoEngine.getWorldX(iter.x()); -+ final int wy = GeoEngine.getWorldY(iter.y()); - - prim.addPoint(Color.RED, wx, wy, z); - } -@@ -53,21 +53,21 @@ - - public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); -- int wy = GeoEngine.getInstance().getWorldY(prevY); -+ int wx = GeoEngine.getWorldX(prevX); -+ int wy = GeoEngine.getWorldY(prevY); - int wz = iter.z(); - prim.addPoint(Color.RED, wx, wy, wz); - -@@ -78,8 +78,8 @@ - - if ((curX != prevX) || (curY != prevY)) - { -- wx = GeoEngine.getInstance().getWorldX(curX); -- wy = GeoEngine.getInstance().getWorldY(curY); -+ wx = GeoEngine.getWorldX(curX); -+ wy = GeoEngine.getWorldY(curY); - wz = iter.z(); - - prim.addPoint(Color.RED, wx, wy, wz); -@@ -93,7 +93,7 @@ - - private static Color getDirectionColor(int x, int y, int z, int nswe) - { -- if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) -+ if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) - { - return Color.GREEN; - } -@@ -109,9 +109,8 @@ - int iPacket = 0; - - ExServerPrimitive exsp = null; -- final GeoEngine ge = GeoEngine.getInstance(); -- final int playerGx = ge.getGeoX(player.getX()); -- final int playerGy = ge.getGeoY(player.getY()); -+ final int playerGx = GeoEngine.getGeoX(player.getX()); -+ final int playerGy = GeoEngine.getGeoY(player.getY()); - for (int dx = -geoRadius; dx <= geoRadius; ++dx) - { - for (int dy = -geoRadius; dy <= geoRadius; ++dy) -@@ -135,12 +134,12 @@ - final int gx = playerGx + dx; - final int gy = playerGy + dy; - -- final int x = ge.getWorldX(gx); -- final int y = ge.getWorldY(gy); -- final int z = ge.getNearestZ(gx, gy, player.getZ()); -+ final int x = GeoEngine.getWorldX(gx); -+ final int y = GeoEngine.getWorldY(gy); -+ final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); - - // north arrow -- Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); -+ Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); - 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); -@@ -147,7 +146,7 @@ - exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z); - - // east arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); - 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); -@@ -154,13 +153,13 @@ - exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z); - - // south arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); - 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, Cell.NSWE_WEST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); - 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); -@@ -188,15 +187,15 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_EAST; -+ return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_EAST; -+ return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; - } - else - { -- return Cell.NSWE_EAST; -+ return GeoStructure.CELL_FLAG_E; // Direction.EAST; - } - } - else if (x < lastX) // west -@@ -203,26 +202,27 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_WEST; -+ return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_WEST; -+ return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; - } - else - { -- return Cell.NSWE_WEST; -+ return GeoStructure.CELL_FLAG_W; // Direction.WEST; - } - } -- else // unchanged x -+ else -+ // unchanged x - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH; -+ return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH; -+ return GeoStructure.CELL_FLAG_N; // Direction.NORTH; - } - else - { -Index: java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java -=================================================================== ---- java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (nonexistent) -+++ java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (working copy) -@@ -0,0 +1,386 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.tools.geodataconverter; -+ -+import java.io.BufferedOutputStream; -+import java.io.File; -+import java.io.FileOutputStream; -+import java.io.RandomAccessFile; -+import java.nio.ByteOrder; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.Scanner; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.commons.util.PropertiesParser; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoDataConverter -+{ -+ private static GeoFormat _format; -+ private static ABlock[][] _blocks; -+ -+ public static void main(String[] args) -+ { -+ // initialize config -+ loadGeoengineConfigs(); -+ -+ // get geodata format -+ String type = ""; -+ try (Scanner scn = new Scanner(System.in)) -+ { -+ while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) -+ { -+ System.out.println("GeoDataConverter: Select source geodata type:"); -+ System.out.println(" J: L2J (e.g. 23_22.l2j)"); -+ System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); -+ System.out.println(" E: Exit"); -+ System.out.print("Choice: "); -+ type = scn.next(); -+ } -+ } -+ if (type.equalsIgnoreCase("E")) -+ { -+ System.exit(0); -+ } -+ -+ _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; -+ -+ // start conversion -+ System.out.println("GeoDataConverter: Converting all " + _format + " files."); -+ -+ // initialize geodata container -+ _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files -+ int converted = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) -+ { -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) -+ { -+ final String input = String.format(_format.getFilename(), rx, ry); -+ final String filepath = Config.GEODATA_PATH; -+ final File f = new File(filepath + input); -+ if (f.exists() && !f.isDirectory()) -+ { -+ // load geodata -+ if (!loadGeoBlocks(input)) -+ { -+ System.out.println("GeoDataConverter: Unable to load " + input + " region file."); -+ continue; -+ } -+ -+ // recalculate nswe -+ if (!recalculateNswe()) -+ { -+ System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); -+ continue; -+ } -+ -+ // save geodata -+ final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); -+ if (!saveGeoBlocks(output)) -+ { -+ System.out.println("GeoDataConverter: Unable to save " + output + " region file."); -+ continue; -+ } -+ -+ converted++; -+ System.out.println("GeoDataConverter: Created " + output + " region file."); -+ } -+ } -+ } -+ System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); -+ } -+ -+ /** -+ * Loads geo blocks from buffer of the region file. -+ * @param filename : The name of the to load. -+ * @return boolean : True when successful. -+ */ -+ private static boolean loadGeoBlocks(String filename) -+ { -+ // region file is load-able, try to load it -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) -+ { -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) -+ if (_format == GeoFormat.L2OFF) -+ { -+ for (int i = 0; i < 18; i++) -+ { -+ buffer.get(); -+ } -+ } -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ if (_format == GeoFormat.L2J) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2J: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2J: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ else -+ { -+ // get block type -+ final short type = buffer.getShort(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ default: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ if (buffer.remaining() > 0) -+ { -+ System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ return false; -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); -+ return false; -+ } -+ } -+ -+ /** -+ * Recalculate diagonal flags for the region file. -+ * @return boolean : True when successful. -+ */ -+ private static boolean recalculateNswe() -+ { -+ try -+ { -+ for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) -+ { -+ for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) -+ { -+ // get block -+ final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // skip flat blocks -+ if (block instanceof BlockFlat) -+ { -+ continue; -+ } -+ -+ // for complex and multilayer blocks go though all layers -+ short height = Short.MAX_VALUE; -+ int index; -+ while ((index = block.getIndexBelow(x, y, height)) != -1) -+ { -+ // get height and nswe -+ height = block.getHeight(index); -+ byte nswe = block.getNswe(index); -+ -+ // update nswe with diagonal flags -+ nswe = updateNsweBelow(x, y, height, nswe); -+ -+ // set nswe of the cell -+ block.setNswe(index, nswe); -+ } -+ } -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ /** -+ * Updates the NSWE flag with diagonal flags. -+ * @param x : Geodata X coordinate. -+ * @param y : Geodata Y coordinate. -+ * @param z : Geodata Z coordinate. -+ * @param nsweValue : NSWE flag to be updated. -+ * @return byte : Updated NSWE flag. -+ */ -+ private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) -+ { -+ byte nswe = nsweValue; -+ -+ // calculate virtual layer height -+ final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); -+ -+ // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) -+ final byte nsweN = getNsweBelow(x, y - 1, height); -+ final byte nsweS = getNsweBelow(x, y + 1, height); -+ final byte nsweW = getNsweBelow(x - 1, y, height); -+ final byte nsweE = getNsweBelow(x + 1, y, height); -+ -+ // north-west -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NW; -+ } -+ -+ // north-east -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NE; -+ } -+ -+ // south-west -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SW; -+ } -+ -+ // south-east -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SE; -+ } -+ -+ return nswe; -+ } -+ -+ private static byte getNsweBelow(int geoX, int geoY, short worldZ) -+ { -+ // out of geo coordinates -+ if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) -+ { -+ return 0; -+ } -+ -+ // out of geo coordinates -+ if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) -+ { -+ return 0; -+ } -+ -+ // get block -+ final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // get index, when valid, return nswe -+ final int index = block.getIndexBelow(geoX, geoY, worldZ); -+ return index == -1 ? 0 : block.getNswe(index); -+ } -+ -+ /** -+ * Save region file to file. -+ * @param filename : The name of file to save. -+ * @return boolean : True when successful. -+ */ -+ private static boolean saveGeoBlocks(String filename) -+ { -+ try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) -+ { -+ // loop over region blocks and save each block -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[ix][iy].saveBlock(bos); -+ } -+ } -+ -+ // flush data to file -+ bos.flush(); -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ private static void loadGeoengineConfigs() -+ { -+ final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); -+ Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); -+ Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); -+ Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); -+ Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); -+ Config.PATHFINDING = geoData.getBoolean("PathFinding", true); -+ Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -+ Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); -+ Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); -+ Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); -+ Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); -+ Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); -+ } -+} -\ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/dist/game/data/geodata/Readme.txt b/L2J_Mobius_Classic_Interlude/dist/game/data/geodata/Readme.txt index a480c8c380..2611882e56 100644 --- a/L2J_Mobius_Classic_Interlude/dist/game/data/geodata/Readme.txt +++ b/L2J_Mobius_Classic_Interlude/dist/game/data/geodata/Readme.txt @@ -23,8 +23,8 @@ a - Prerequisites b - Make it work ---------------------------------------------- -To make geodata working: -* unpack your geodata files into "/data/geodata" folder -* open "/config/GeoEngine.ini" with your favorite text editor and then edit following config: - - CoordSynchronize = 2 -* If you do not use any geodata files, the server will automatically change this setting to -1. +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. diff --git a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java index 619ba36405..76335d113f 100644 --- a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java +++ b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/ai/areas/ImperialTomb/FourSepulchers/FourSepulchers.java @@ -298,7 +298,7 @@ public class FourSepulchers extends AbstractNpcAI implements IXmlReader { if ((npc != null) && !npc.isDead()) { - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), npc.getSpawn().getLocation().getX() + getRandom(-400, 400), npc.getSpawn().getLocation().getY() + getRandom(-400, 400), npc.getZ(), npc.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), npc.getSpawn().getLocation().getX() + getRandom(-400, 400), npc.getSpawn().getLocation().getY() + getRandom(-400, 400), npc.getZ(), npc.getInstanceWorld()); if (Util.calculateDistance(npc, npc.getSpawn().getLocation(), false, false) < 600) { npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); diff --git a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java index c18647136f..9da84854cf 100644 --- a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java +++ b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/ai/areas/PrimevalIsle/PrimevalIsle.java @@ -270,7 +270,7 @@ public class PrimevalIsle extends AbstractNpcAI final double cos = Math.cos(radian); final int newX = (int) (npc.getX() + (cos * distance)); final int newY = (int) (npc.getY() + (sin * distance)); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, npc.getZ(), npc.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, npc.getZ(), npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, loc, 0); } else if (ag_type == 1) diff --git a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/ai/others/FleeMonsters.java b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/ai/others/FleeMonsters.java index 4562336103..a971742033 100644 --- a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/ai/others/FleeMonsters.java +++ b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/ai/others/FleeMonsters.java @@ -59,7 +59,7 @@ public class FleeMonsters extends AbstractNpcAI final int posX = (int) (npc.getX() + (FLEE_DISTANCE * Math.cos(radians))); final int posY = (int) (npc.getY() + (FLEE_DISTANCE * Math.sin(radians))); final int posZ = npc.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, attacker.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); return super.onAttack(npc, attacker, damage, isSummon); } diff --git a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/ai/others/RandomWalkingGuards.java b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/ai/others/RandomWalkingGuards.java index b99becfdc6..780502d5d6 100644 --- a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/ai/others/RandomWalkingGuards.java +++ b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/ai/others/RandomWalkingGuards.java @@ -55,7 +55,7 @@ public class RandomWalkingGuards extends AbstractNpcAI if (!npc.isInCombat()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, Config.MAX_DRIFT_RANGE); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("RANDOM_WALK", getRandom(MIN_WALK_DELAY, MAX_WALK_DELAY), npc, null); } diff --git a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index 524652cbc1..18511c6541 100644 --- a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { @@ -133,8 +133,8 @@ public class AdminGeodata implements IAdminCommandHandler } case "admin_geomap": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y + " (" + ((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + "," + ((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + " to " + ((((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + "," + ((((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + ")"); break; } diff --git a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java index f3dffa1227..76572f7f25 100644 --- a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java +++ b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java @@ -57,8 +57,8 @@ public class AdminMissingHtmls implements IAdminCommandHandler { case "admin_geomap_missing_htmls": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final int topLeftX = (x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE; final int topLeftY = (y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE; final int bottomRightX = (((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1; diff --git a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index fdb2319138..a0a34f1051 100644 --- a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -19,9 +19,9 @@ package handlers.admincommandhandlers; import java.util.List; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; +import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.util.BuilderUtil; @@ -44,13 +44,13 @@ public class AdminPathNode implements IAdminCommandHandler } if (activeChar.getTarget() != null) { - final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { BuilderUtil.sendSysMessage(activeChar, "No Route!"); return true; } - for (AbstractNodeLoc a : path) + for (Location a : path) { BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } diff --git a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/Blink.java b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/Blink.java index 22f2c84eae..22697e8e20 100644 --- a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/Blink.java +++ b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/Blink.java @@ -88,7 +88,7 @@ public class Blink extends AbstractEffect final int y = effected.getY() + y1; final int z = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, destination, _flyType, _flySpeed, _flyDelay, _animationSpeed)); diff --git a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/Fear.java b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/Fear.java index 95a5afdf98..88c795e04b 100644 --- a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/Fear.java +++ b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/Fear.java @@ -100,7 +100,7 @@ public class Fear extends AbstractEffect final int posY = (int) (effected.getY() + (FEAR_RANGE * Math.sin(radians))); final int posZ = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); } } diff --git a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java index fddd94c60a..55c2e78d8a 100644 --- a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java +++ b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java @@ -57,7 +57,7 @@ public class FlyAway extends AbstractEffect final int y = (int) (effector.getY() - (nRadius * (dy / distance))); final int z = effector.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.broadcastPacket(new FlyToLocation(effected, destination, FlyType.THROW_UP)); effected.setXYZ(destination); diff --git a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java index 1fbd8239b0..89ad8670dc 100644 --- a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java +++ b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java @@ -133,7 +133,7 @@ public class KnockBack extends AbstractEffect final int x = (int) (effected.getX() + (_distance * Math.cos(radians))); final int y = (int) (effected.getY() + (_distance * Math.sin(radians))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, loc, _type, _speed, _delay, _animationSpeed)); diff --git a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/PullBack.java b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/PullBack.java index ed6a37eff6..38fe2fe9d0 100644 --- a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/PullBack.java +++ b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/PullBack.java @@ -73,7 +73,7 @@ public class PullBack extends AbstractEffect } // In retail, you get debuff, but you are not even moved if there is obstacle. You are still disabled from using skills and moving though. - if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effector.getInstanceWorld())) + if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effected.getInstanceWorld())) { effected.broadcastPacket(new FlyToLocation(effected, effector, _type, _speed, _delay, _animationSpeed)); effected.setXYZ(effector.getX(), effector.getY(), GeoEngine.getInstance().getHeight(effector.getX(), effector.getY(), effector.getZ()) + 10); diff --git a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java index d6f9664141..813e496c47 100644 --- a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java +++ b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java @@ -87,7 +87,7 @@ public class TeleportToSummon extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = summon.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java index d3c263978c..c9dc34ac22 100644 --- a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java +++ b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java @@ -76,7 +76,7 @@ public class TeleportToTarget extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java index f2148adb54..c7ca6f64cb 100644 --- a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java +++ b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java @@ -65,7 +65,7 @@ public class RollingDice implements IItemHandler final int x = player.getX() + x1; final int y = player.getY() + y1; final int z = player.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); Broadcast.toSelfAndKnownPlayers(player, new Dice(player.getObjectId(), itemId, number, destination.getX(), destination.getY(), destination.getZ())); final SystemMessage sm = new SystemMessage(SystemMessageId.C1_HAS_ROLLED_A_S2); diff --git a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/targethandlers/Ground.java b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/targethandlers/Ground.java index 82ab321c29..d8c73627f5 100644 --- a/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/targethandlers/Ground.java +++ b/L2J_Mobius_Classic_Interlude/dist/game/data/scripts/handlers/targethandlers/Ground.java @@ -52,7 +52,7 @@ public class Ground implements ITargetTypeHandler return null; } - if (!GeoEngine.getInstance().canSeeTarget(creature, worldPosition)) + if (!GeoEngine.getInstance().canSeeLocation(creature, worldPosition)) { if (sendMessage) { diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/Config.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/Config.java index 155890bbb4..51e71ade13 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/Config.java @@ -61,6 +61,7 @@ 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,12 +908,17 @@ public class Config // GeoEngine // -------------------------------------------------- public static Path GEODATA_PATH; + public static GeoType GEODATA_TYPE; public static boolean PATHFINDING; public static String PATHFIND_BUFFERS; - public static float LOW_WEIGHT; - public static float MEDIUM_WEIGHT; - public static float HIGH_WEIGHT; - public static float DIAGONAL_WEIGHT; + public static int MOVE_WEIGHT; + public static int MOVE_WEIGHT_DIAG; + public static int OBSTACLE_WEIGHT; + 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; /** Attribute System */ public static int S_WEAPON_STONE; @@ -2417,12 +2423,17 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); + GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); - MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); - HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); - DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); + MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); + OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); + HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); + MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); + MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); // Load AllowedPlayerRaces config file (if exists) final PropertiesParser AllowedPlayerRaces = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE); diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/commons/util/CommonUtil.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/commons/util/CommonUtil.java index 14ae3a4560..ab7837d827 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/commons/util/CommonUtil.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/commons/util/CommonUtil.java @@ -584,4 +584,15 @@ public class CommonUtil final DecimalFormat formatter = new DecimalFormat(format, new DecimalFormatSymbols(Locale.ENGLISH)); return formatter.format(value); } + + /** + * @param numToTest : The number to test. + * @param min : The minimum limit. + * @param max : The maximum limit. + * @return the number or one of the limit (mininum / maximum). + */ + public static int limit(int numToTest, int min, int max) + { + return (numToTest > max) ? max : ((numToTest < min) ? min : numToTest); + } } diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/commons/util/Point2D.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/commons/util/Point2D.java new file mode 100644 index 0000000000..ebb6272e5e --- /dev/null +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/commons/util/Point2D.java @@ -0,0 +1,180 @@ +/* + * 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 . + */ +package org.l2jmobius.commons.util; + +import java.util.Objects; + +/** + * A datatype used to retain a 2D (x/y) point. It got the capability to be set and cleaned. + */ +public class Point2D +{ + protected volatile int _x; + protected volatile int _y; + + public Point2D(int x, int y) + { + _x = x; + _y = y; + } + + @Override + public Point2D clone() + { + return new Point2D(_x, _y); + } + + @Override + public String toString() + { + return _x + ", " + _y; + } + + @Override + public int hashCode() + { + return Objects.hash(_x, _y); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final Point2D other = (Point2D) obj; + return (_x == other._x) && (_y == other._y); + } + + /** + * @param x : The X coord to test. + * @param y : The Y coord to test. + * @return True if all coordinates equals this {@link Point2D} coordinates. + */ + public boolean equals(int x, int y) + { + return (_x == x) && (_y == y); + } + + public int getX() + { + return _x; + } + + public void setX(int x) + { + _x = x; + } + + public int getY() + { + return _y; + } + + public void setY(int y) + { + _y = y; + } + + public void set(int x, int y) + { + _x = x; + _y = y; + } + + /** + * Refresh the current {@link Point2D} using a reference {@link Point2D} and a distance. The new destination is calculated to go in opposite side of the {@link Point2D} reference.
+ *
+ * This method is perfect to calculate fleeing characters position. + * @param referenceLoc : The Point2D used as position. + * @param distance : The distance to be set between current and new position. + */ + public void setFleeing(Point2D referenceLoc, int distance) + { + final double xDiff = referenceLoc.getX() - _x; + final double yDiff = referenceLoc.getY() - _y; + + final double yxRation = Math.abs(xDiff / yDiff); + + final int y = (int) (distance / (yxRation + 1)); + final int x = (int) (y * yxRation); + + _x += (xDiff < 0 ? x : -x); + _y += (yDiff < 0 ? y : -y); + } + + public void clean() + { + _x = 0; + _y = 0; + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @return The distance between this {@Point2D} and some given coordinates. + */ + public double distance2D(int x, int y) + { + final double dx = (double) _x - x; + final double dy = (double) _y - y; + + return Math.sqrt((dx * dx) + (dy * dy)); + } + + /** + * @param point : The {@link Point2D} to test. + * @return The distance between this {@Point2D} and the {@link Point2D} set as parameter. + */ + public double distance2D(Point2D point) + { + return distance2D(point.getX(), point.getY()); + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of some given coordinates. + */ + public boolean isIn2DRadius(int x, int y, int radius) + { + return distance2D(x, y) < radius; + } + + /** + * @param point : The Point2D to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of the {@link Point2D} set as parameter. + */ + public boolean isIn2DRadius(Point2D point, int radius) + { + return distance2D(point) < radius; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/AttackableAI.java index b6cb5fa2a8..c0d4d5fc50 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/AttackableAI.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/ai/AttackableAI.java @@ -578,7 +578,7 @@ public class AttackableAI extends CreatureAI } // Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast) - final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); + final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); moveTo(moveLoc.getX(), moveLoc.getY(), moveLoc.getZ()); } } @@ -801,7 +801,7 @@ public class AttackableAI extends CreatureAI final int newZ = npc.getZ() + 30; // Mobius: Verify destination. Prevents wall collision issues and fixes monsters not avoiding obstacles. - moveTo(GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); + moveTo(GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); } return; } diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/data/SpawnTable.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/data/SpawnTable.java index a81201f69e..19f0db5b3e 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/data/SpawnTable.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/data/SpawnTable.java @@ -114,8 +114,8 @@ public class SpawnTable } // XML file for spawn - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); // Write info to XML @@ -192,8 +192,8 @@ public class SpawnTable if (update) { - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = spawn.getNpcSpawnTemplate() != null ? spawn.getNpcSpawnTemplate().getSpawnTemplate().getFile() : new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); final File tempFile = new File(spawnFile.getAbsolutePath().substring(Config.DATAPACK_ROOT.getAbsolutePath().length() + 1).replace('\\', '/') + ".tmp"); try diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/enums/GeoType.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/enums/GeoType.java new file mode 100644 index 0000000000..f77aa5eac4 --- /dev/null +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/enums/GeoType.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.enums; + +public enum GeoType +{ + L2J("%d_%d.l2j"), + L2OFF("%d_%d_conv.dat"); + + private final String _filename; + + private GeoType(String filename) + { + _filename = filename; + } + + public String getFilename() + { + return _filename; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java new file mode 100644 index 0000000000..e62f50a8df --- /dev/null +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java @@ -0,0 +1,144 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 9d255a5f54..7bef9c770b 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,72 +16,63 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.channels.FileChannel.MapMode; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.logging.Level; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; import java.util.logging.Logger; import org.l2jmobius.Config; +import org.l2jmobius.commons.util.CommonUtil; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; -import org.l2jmobius.gameserver.geoengine.geodata.IRegion; -import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; -import org.l2jmobius.gameserver.geoengine.geodata.Region; +import org.l2jmobius.gameserver.enums.GeoType; +import org.l2jmobius.gameserver.enums.MoveDirectionType; +import org.l2jmobius.gameserver.geoengine.geodata.ABlock; +import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; +import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; +import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; +import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.model.interfaces.ILocational; -import org.l2jmobius.gameserver.util.GeoUtils; -import org.l2jmobius.gameserver.util.LinePointIterator; -import org.l2jmobius.gameserver.util.LinePointIterator3D; -/** - * @author -Nemesiss-, HorridoJoho - */ public class GeoEngine { - private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - - private static final int WORLD_MIN_X = -655360; - private static final int WORLD_MIN_Y = -589824; - private static final int WORLD_MIN_Z = -16384; - - /** 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; - - /** The regions array */ - private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; - private static final int ELEVATED_SEE_OVER_DISTANCE = 2; - private static final int MAX_SEE_OVER_HEIGHT = 48; - protected GeoEngine() + private final ABlock[][] _blocks; + private final BlockNull _nullBlock; + + // Pre-allocated buffers. + private final BufferHolder[] _buffers; + + public GeoEngine() { LOGGER.info("GeoEngine: Initializing..."); - for (int i = 0; i < _regions.length(); i++) - { - _regions.set(i, NullRegion.INSTANCE); - } + // Initialize block container. + _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; + + // Load null block. + _nullBlock = new BlockNull(); + + // Initialize multilayer temporarily buffer. + BlockMultilayer.initialize(); + + // Load geo files according to geoengine config setup. int loaded = 0; try { @@ -92,23 +83,52 @@ public class GeoEngine final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); if (Files.exists(geoFilePath)) { - try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + // Region file is load-able, try to load it. + if (loadGeoBlocks(regionX, regionY)) { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); loaded++; } } + else + { + // Region file is not load-able, load null blocks. + loadNullBlocks(regionX, regionY); + } } } } catch (Exception e) { - LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + LOGGER.warning("GeoEngine: Failed to load geodata! " + e); System.exit(1); } - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + // Release multilayer block temporarily buffer. + BlockMultilayer.release(); + + String[] array = Config.PATHFIND_BUFFERS.split(";"); + _buffers = new BufferHolder[array.length]; + + int count = 0; + for (int i = 0; i < array.length; i++) + { + String buf = array[i]; + String[] args = buf.split("x"); + + try + { + int size = Integer.parseInt(args[1]); + count += size; + _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); + } + catch (Exception e) + { + LOGGER.warning("Could not load buffer setting:" + buf + ". " + e); + } + } + LOGGER.info("Loaded " + count + " node buffers."); + // Avoid wrong configs when no files are loaded. if ((loaded == 0) && Config.PATHFINDING) { @@ -118,616 +138,913 @@ public class GeoEngine } /** - * @param geoX - * @param geoY - * @return the region + * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer and log this situation. + * @param size : pre-calculated minimal required size + * @return NodeBuffer : buffer */ - private IRegion getRegion(int geoX, int geoY) + private NodeBuffer getBuffer(int size) { - final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); - if ((region < 0) || (region >= _regions.length())) + NodeBuffer current = null; + for (BufferHolder holder : _buffers) { - return null; - } - return _regions.get(region); - } - - /** - * @param filePath - * @param regionX - * @param regionY - * @throws IOException - */ - 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()).load().order(ByteOrder.LITTLE_ENDIAN))); - } - } - - /** - * @param regionX - * @param regionY - */ - public void unloadRegion(int regionX, int regionY) - { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); - } - - /** - * @param geoX - * @param geoY - * @return if geodata exist - */ - public boolean hasGeoPos(int geoX, int geoY) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return false; - } - return region.hasGeo(); - } - - /** - * Checks the specified position for available geodata. - * @param x the world x - * @param y the world y - * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise - */ - public boolean hasGeo(int x, int y) - { - return hasGeoPos(getGeoX(x), getGeoY(y)); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe check - */ - public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return true; - } - return region.checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe anti-corner cut - */ - public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) - { - boolean can = true; - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - return can && checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the nearest Z value - */ - public int getNearestZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNearestZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next lower Z value - */ - public int getNextLowerZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextLowerZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next higher Z value - */ - public int getNextHigherZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextHigherZ(geoX, geoY, worldZ); - } - - /** - * Gets the Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHeight(int x, int y, int z) - { - return getNearestZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next lower Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getLowerHeight(int x, int y, int z) - { - return getNextLowerZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next higher Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHigherHeight(int x, int y, int z) - { - return getNextHigherZ(getGeoX(x), getGeoY(y), z); - } - - /** - * @param worldX - * @return the geo X - */ - public int getGeoX(int worldX) - { - return (worldX - WORLD_MIN_X) / 16; - } - - /** - * @param worldY - * @return the geo Y - */ - public int getGeoY(int worldY) - { - return (worldY - WORLD_MIN_Y) / 16; - } - - /** - * @param worldZ - * @return the geo Z - */ - public int getGeoZ(int worldZ) - { - return (worldZ - WORLD_MIN_Z) / 16; - } - - /** - * @param geoX - * @return the world X - */ - public int getWorldX(int geoX) - { - return (geoX * 16) + WORLD_MIN_X + 8; - } - - /** - * @param geoY - * @return the world Y - */ - public int getWorldY(int geoY) - { - return (geoY * 16) + WORLD_MIN_Y + 8; - } - - /** - * @param geoZ - * @return the world Z - */ - public int getWorldZ(int geoZ) - { - return (geoZ * 16) + WORLD_MIN_Z + 8; - } - - /** - * Can see target. Doors as target always return true. Checks doors between. - * @param cha the character - * @param target the target - * @return {@code true} if the character can see the target (LOS), {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, WorldObject target) - { - if (target.isDoor()) - { - // Can always see doors. - return true; - } - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); - } - - /** - * Can see target. Checks doors between. - * @param cha the character - * @param worldPosition the world position - * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) - { - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param world - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @param tworld the target's instanceId - * @return - */ - public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) - { - return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instance - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) - { - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) - { - return false; - } - return canSeeTarget(x, y, z, tx, ty, tz); - } - - /** - * @param prevX - * @param prevY - * @param prevGeoZ - * @param curX - * @param curY - * @param nswe - * @return the LOS Z value - */ - private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) - { - if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) - { - throw new RuntimeException("Multiple directions!"); - } - - if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) - { - return getNearestZ(curX, curY, prevGeoZ); - } - - return getNextHigherZ(curX, curY, prevGeoZ); - } - - /** - * Can see target. Does not check doors between. - * @param xValue the x coordinate - * @param yValue the y coordinate - * @param zValue the z coordinate - * @param txValue the target's x coordinate - * @param tyValue the target's y coordinate - * @param tzValue the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) - { - int x = xValue; - int y = yValue; - int tx = txValue; - int ty = tyValue; - - int geoX = getGeoX(x); - int geoY = getGeoY(y); - int tGeoX = getGeoX(tx); - int tGeoY = getGeoY(ty); - - int z = getNearestZ(geoX, geoY, zValue); - int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - // Fastpath. - if ((geoX == tGeoX) && (geoY == tGeoY)) - { - if (hasGeoPos(tGeoX, tGeoY)) - { - return z == tz; - } - return true; - } - - if (tz > z) - { - int tmp = tx; - tx = x; - x = tmp; - - tmp = ty; - ty = y; - y = tmp; - - tmp = tz; - tz = z; - z = tmp; - - tmp = tGeoX; - tGeoX = geoX; - geoX = tmp; - - tmp = tGeoY; - tGeoY = geoY; - geoY = tmp; - } - - final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); - // First point is guaranteed to be available, skip it, we can always see our own position. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - final int prevZ = pointIter.z(); - int prevGeoZ = prevZ; - int ptIndex = 0; - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - - if ((curX == prevX) && (curY == prevY)) + // Find proper size of buffer. + if (holder._size < size) { continue; } - final int beeCurZ = pointIter.z(); - int curGeoZ = prevGeoZ; - - // Check if the position has geodata. - if (hasGeoPos(curX, curY)) + // Find unlocked NodeBuffer. + for (NodeBuffer buffer : holder._buffer) { - final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); - curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); - int maxHeight; - if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) + if (!buffer.isLocked()) { - maxHeight = z + MAX_SEE_OVER_HEIGHT; - } - else - { - maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; + continue; } - boolean canSeeThrough = false; - if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) + return buffer; + } + + LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); + + // NodeBuffer not found, allocate temporary buffer. + current = new NodeBuffer(holder._size); + current.isLocked(); + } + + return current; + } + + /** + * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + * @return boolean : True, when geodata file was loaded without problem. + */ + private boolean loadGeoBlocks(int regionX, int regionY) + { + final String filename = String.format(Config.GEODATA_TYPE.getFilename(), regionX, regionY); + final String filepath = Config.GEODATA_PATH + "\\" + filename; + + // Standard load. + try (RandomAccessFile raf = new RandomAccessFile(filepath, "r"); + FileChannel fc = raf.getChannel()) + { + // Initialize file buffer. + MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // Load 18B header for L2OFF geodata (1st and 2nd byte...region X and Y). + if (Config.GEODATA_TYPE == GeoType.L2OFF) + { + for (int i = 0; i < 18; i++) { - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) + buffer.get(); + } + } + + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Loop over region blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + if (Config.GEODATA_TYPE == GeoType.L2J) { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) - { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); + // Get block type. + final byte type = buffer.get(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + case GeoStructure.TYPE_MULTILAYER_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + default: + { + throw new IllegalArgumentException("Unknown block type: " + type); + } + } } else { - canSeeThrough = true; + // Get block type. + final short type = buffer.getShort(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + default: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + } } } + } + + // Check data consistency. + if (buffer.remaining() > 0) + { + LOGGER.warning("Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); + } + + // Loading was successful. + return true; + } + catch (Exception e) + { + // An error occured while loading, load null blocks. + LOGGER.warning("Error loading " + filename + " region file. " + e); + + // Replace whole region file with null blocks. + loadNullBlocks(regionX, regionY); + + // Loading was not successful. + return false; + } + } + + /** + * Loads null blocks. Used when no region file is detected or an error occurs during loading. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + */ + private void loadNullBlocks(int regionX, int regionY) + { + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Load all null blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + _blocks[blockX + ix][blockY + iy] = _nullBlock; + } + } + } + + /** + * Converts world X to geodata X. + * @param worldX + * @return int : Geo X + */ + public static int getGeoX(int worldX) + { + return (worldX - World.WORLD_X_MIN) >> 4; + } + + /** + * Converts world Y to geodata Y. + * @param worldY + * @return int : Geo Y + */ + public static int getGeoY(int worldY) + { + return (worldY - World.WORLD_Y_MIN) >> 4; + } + + /** + * Converts geodata X to world X. + * @param geoX + * @return int : World X + */ + public static int getWorldX(int geoX) + { + return (geoX << 4) + World.WORLD_X_MIN + 8; + } + + /** + * Converts geodata Y to world Y. + * @param geoY + * @return int : World Y + */ + public static int getWorldY(int geoY) + { + return (geoY << 4) + World.WORLD_Y_MIN + 8; + } + + /** + * Returns block of geodata on given coordinates. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return {@link ABlock} : Block of geodata. + */ + public ABlock getBlock(int geoX, int geoY) + { + final int x = geoX / GeoStructure.BLOCK_CELLS_X; + if ((x < 0) || (x >= GeoStructure.GEO_BLOCKS_X)) + { + return null; + } + final int y = geoY / GeoStructure.BLOCK_CELLS_Y; + if ((y < 0) || (y >= GeoStructure.GEO_BLOCKS_Y)) + { + return null; + } + return _blocks[x][y]; + } + + /** + * Check if geo coordinates has geo. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return boolean : True, if given geo coordinates have geodata + */ + public boolean hasGeoPos(int geoX, int geoY) + { + final ABlock block = getBlock(geoX, geoY); + return (block != null) && block.hasGeoPos(); + } + + /** + * Returns the height of cell, which is closest to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell geodata Z coordinate, closest to given coordinates. + */ + public short getHeightNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return (short) worldZ; + } + return block.getHeightNearest(geoX, geoY, worldZ); + } + + /** + * Returns the NSWE flag byte of cell, which is closes to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. + */ + public byte getNsweNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return GeoStructure.CELL_FLAG_ALL; + } + return block.getNsweNearest(geoX, geoY, worldZ); + } + + /** + * Check if world coordinates has geo. + * @param worldX : World X + * @param worldY : World Y + * @return boolean : True, if given world coordinates have geodata + */ + public boolean hasGeo(int worldX, int worldY) + { + return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param loc : The location used as reference. + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(Location loc) + { + return getHeightNearest(getGeoX(loc.getX()), getGeoY(loc.getY()), loc.getZ()); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param worldX : world x + * @param worldY : world y + * @param worldZ : world z + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(int worldX, int worldY, int worldZ) + { + return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); + } + + /** + * Check line of sight from {@link WorldObject} to {@link WorldObject}.
+ * @param object : The origin object. + * @param target : The target object. + * @return True, when object can see target. + */ + public boolean canSeeTarget(WorldObject object, WorldObject target) + { + // Can always see doors. + if (target.isDoor()) + { + return true; + } + + if (object.getInstanceWorld() != target.getInstanceWorld()) + { + return false; + } + + if (DoorData.getInstance().checkIfDoorsBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld())) + { + return false; + } + + // Get object's and target's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + double theight = 0; + if (target instanceof Creature) + { + theight += (((Creature) target).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + return canSee(object.getX(), object.getY(), object.getZ(), oheight, target.getX(), target.getY(), target.getZ(), theight) && canSee(target.getX(), target.getY(), target.getZ(), theight, object.getX(), object.getY(), object.getZ(), oheight); + } + + /** + * Check line of sight from {@link WorldObject} to {@link Location}.
+ * Note: The check uses {@link Location}'s real Z coordinate (e.g. point above ground), not its geodata representation. + * @param object : The origin object. + * @param position : The target position. + * @return True, when object can see position. + */ + public boolean canSeeLocation(WorldObject object, Location position) + { + // Get object and location coordinates. + int ox = object.getX(); + int oy = object.getY(); + int oz = object.getZ(); + int tx = position.getX(); + int ty = position.getY(); + int tz = position.getZ(); + + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld())) + { + return false; + } + + // Get object's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + // Perform geodata check. + return canSee(ox, oy, oz, oheight, tx, ty, tz, 0) && canSee(tx, ty, tz, 0, ox, oy, oz, oheight); + } + + /** + * Simple check for origin to target visibility.
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param oheight : The height of origin, used as start point. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param theight : The height of target, used as end point. + * @return True, when origin can see target. + */ + public boolean canSee(int ox, int oy, int oz, double oheight, int tx, int ty, int tz, double theight) + { + // Get origin geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get target geodata coordinates. + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check being on same cell and layer (index). + // Note: Get index must use origin height increased by cell height, the method returns index to height exclusive self. + int index = block.getIndexBelow(gox, goy, oz + GeoStructure.CELL_HEIGHT); + if (index < 0) + { + return false; + } + + if ((gox == gtx) && (goy == gty)) + { + return index == block.getIndexBelow(gtx, gty, tz + GeoStructure.CELL_HEIGHT); + } + + // Get ground and nswe flag. + int groundZ = block.getHeight(index); + int nswe = block.getNswe(index); + + // Get delta coordinates, slope of line (XY, XZ) and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double dz = (tz + theight) - (oz + oheight); + final double m = (double) dy / dx; + final double mz = dz / Math.sqrt((dx * dx) + (dy * dy)); + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); + + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) + { + // Set next cell in X direction. + gridX += mdt.getStepX(); + gox += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); - if (!canSeeThrough) - { - return false; - } + // Set next cell in Y direction. + gridY += mdt.getStepY(); + goy += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevGeoZ = curGeoZ; - ++ptIndex; + // Get block of the next cell. + block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get line of sight height (including Z slope). + double losz = oz + oheight + Config.MAX_OBSTACLE_HEIGHT; + losz += mz * Math.sqrt(((checkX - ox) * (checkX - ox)) + ((checkY - oy) * (checkY - oy))); + + // Check line of sight going though wall (vertical check). + + // Get index of particular layer, based on last iterated cell conditions. + boolean canMove = (nswe & dir) != 0; + if (canMove) + { + // No wall present, get next cell below current cell. + index = block.getIndexBelow(gox, goy, groundZ + GeoStructure.CELL_IGNORE_HEIGHT); + } + else + { + // Wall present, get next cell above current cell. + index = block.getIndexAbove(gox, goy, groundZ - (2 * GeoStructure.CELL_HEIGHT)); + } + + // Next cell's does not exist (no geodata with valid condition), return fail. + if (index < 0) + { + return false; + } + + // Get next cell's layer height. + int z = block.getHeight(index); + + // Perform sine of sight check (next cell is above line of sight line), return fail. + if (z > losz) + { + return false; + } + + // Next cell is accessible, update z and NSWE. + groundZ = z; + nswe = block.getNswe(index); } + // Iteration is completed, no obstacle is found. return true; } /** - * Move check. - * @param x the x coordinate - * @param y the y coordinate - * @param zValue the z coordinate - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tzValue the target's z coordinate - * @param instance the instance - * @return the last Location (x,y,z) where player can walk - just before wall + * Check movement from coordinates to coordinates.
+ * Note: The Z coordinates are supposed to be already validated geodata coordinates. + * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return True, when target coordinates are reachable from origin coordinates. */ - public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(x); - final int geoY = getGeoY(y); - final int z = getNearestZ(geoX, geoY, zValue); - final int tGeoX = getGeoX(tx); - final int tGeoY = getGeoY(ty); - final int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return new Location(x, y, getHeight(x, y, z)); - } - if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) - { - return new Location(x, y, getHeight(x, y, z)); + return false; } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = z; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return false; + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + int goz = getHeightNearest(gox, goy, oz); + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check movement within same cell. + if ((gox == gtx) && (goy == gty)) + { + return goz == getHeight(tx, ty, tz); + } + + // Get nswe flag. + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - // Can't move, return previous location. - return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check point heading into obstacle, if so return current point. + if ((nswe & dir) == 0) + { + return false; + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return false; + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != tz)) - { - // Different floors, return start location. - return new Location(x, y, z); - } - - return new Location(tx, ty, tz); + // When origin Z is target Z, the move is successful. + return goz == getHeight(tx, ty, tz); } /** - * Checks if its possible to move from one location to another. - * @param fromX the X coordinate to start checking from - * @param fromY the Y coordinate to start checking from - * @param fromZvalue the Z coordinate to start checking from - * @param toX the X coordinate to end checking at - * @param toY the Y coordinate to end checking at - * @param toZvalue the Z coordinate to end checking at - * @param instance the instance - * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + * Check movement from origin to target coordinates. Returns last available point in the checked path.
+ * Target X and Y reachable and Z is on same floor: + *
    + *
  • Location of the target with corrected Z value from geodata.
  • + *
+ * Target X and Y reachable but Z is on another floor: + *
    + *
  • Location of the origin with corrected Z value from geodata.
  • + *
+ * Target X and Y not reachable: + *
    + *
  • Last accessible location in destination to target.
  • + *
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return The {@link Location} representing last point of movement (e.g. just before wall). */ - public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + public Location getValidLocation(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(fromX); - final int geoY = getGeoY(fromY); - final int fromZ = getNearestZ(geoX, geoY, fromZvalue); - final int tGeoX = getGeoX(toX); - final int tGeoY = getGeoY(toY); - final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); - - if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) - { - return false; + return new Location(ox, oy, oz); } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = fromZ; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return new Location(ox, oy, oz); + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + final int gtz = getHeightNearest(gtx, gty, tz); + int goz = getHeightNearest(gox, goy, oz); + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - return false; - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check target cell is out of geodata grid (world coordinates). + if ((nx < 0) || (nx >= GeoStructure.GEO_CELLS_X) || (ny < 0) || (ny >= GeoStructure.GEO_CELLS_Y)) + { + return new Location(checkX, checkY, goz); + } + + // Check point heading into obstacle, if so return current (border) point. + if ((nswe & dir) == 0) + { + return new Location(checkX, checkY, goz); + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current (border) point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return new Location(checkX, checkY, goz); + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) - { - // Different floors. - return false; - } - - return true; + // Compare Z coordinates: + // If same, path is okay, return target point and fix its Z geodata coordinate. + // If not same, path is does not exist, return origin point. + return goz == gtz ? new Location(tx, ty, gtz) : new Location(ox, oy, oz); } + /** + * Returns the list of location objects as a result of complete path calculation. + * @param ox : origin x + * @param oy : origin y + * @param oz : origin z + * @param tx : target x + * @param ty : target y + * @param tz : target z + * @param instance + * @return {@code List} : complete path from nodes + */ + public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + { + // Get origin and check existing geo coords. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + if (!hasGeoPos(gox, goy)) + { + return Collections.emptyList(); + } + + int goz = getHeightNearest(gox, goy, oz); + + // Get target and check existing geo coords. + int gtx = getGeoX(tx); + int gty = getGeoY(ty); + if (!hasGeoPos(gtx, gty)) + { + return Collections.emptyList(); + } + + int gtz = getHeightNearest(gtx, gty, tz); + + // Prepare buffer for pathfinding calculations. + NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + if (buffer == null) + { + return Collections.emptyList(); + } + + // Find path. + List path = null; + try + { + path = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + if (path.isEmpty()) + { + return Collections.emptyList(); + } + } + catch (Exception e) + { + return Collections.emptyList(); + } + finally + { + buffer.free(); + } + + // Check path. + if (path.size() < 3) + { + return path; + } + + // Get path list iterator. + ListIterator point = path.listIterator(); + + // Get node A (origin). + int nodeAx = ox; + int nodeAy = oy; + int nodeAz = goz; + + // Get node B. + Location nodeB = point.next(); + + // Iterate thought the path to optimize it. + while (point.hasNext()) + { + // Get node C. + Location nodeC = path.get(point.nextIndex()); + + // Check movement from node A to node C. + if (canMoveToTarget(nodeAx, nodeAy, nodeAz, nodeC.getX(), nodeC.getY(), nodeC.getZ(), instance)) + { + // Can move from node A to node C. + // Remove node B. + point.remove(); + } + else + { + // Can not move from node A to node C. + // Set node A (node B is part of path, update A coordinates). + nodeAx = nodeB.getX(); + nodeAy = nodeB.getY(); + nodeAz = nodeB.getZ(); + } + + // Set node B. + nodeB = point.next(); + } + + return path; + } + + /** + * NodeBuffer container with specified size and count of separate buffers. + */ + private static class BufferHolder + { + final int _size; + final List _buffer; + + public BufferHolder(int size, int count) + { + _size = size; + _buffer = new ArrayList<>(count); + + for (int i = 0; i < count; i++) + { + _buffer.add(new NodeBuffer(size)); + } + } + } + + /** + * Returns the instance of the {@link GeoEngine}. + * @return {@link GeoEngine} : The instance. + */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -737,4 +1054,4 @@ public class GeoEngine { protected static final GeoEngine INSTANCE = new GeoEngine(); } -} +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java deleted file mode 100644 index cc76b7523a..0000000000 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ /dev/null @@ -1,301 +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 . - */ -package org.l2jmobius.gameserver.geoengine; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; -import org.l2jmobius.gameserver.model.World; -import org.l2jmobius.gameserver.model.instancezone.Instance; - -/** - * @author -Nemesiss- - */ -public class GeoEnginePathfinding -{ - private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); - - private BufferInfo[] _buffers; - - protected GeoEnginePathfinding() - { - try - { - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - - _buffers = 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); - } - - _buffers[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"); - } - } - - public boolean pathNodesExist(short regionoffset) - { - return false; - } - - public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) - { - 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)))); - if (buffer == null) - { - return null; - } - - List path = null; - try - { - final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); - - if (result == null) - { - return null; - } - - path = constructPath(result); - } - catch (Exception e) - { - LOGGER.warning(e.getMessage()); - return null; - } - finally - { - buffer.free(); - } - - // check path - if (path.size() < 3) - { - return path; - } - - int currentX, currentY, currentZ; - ListIterator middlePoint; - - middlePoint = path.listIterator(); - currentX = x; - currentY = y; - currentZ = z; - - while (middlePoint.hasNext()) - { - final AbstractNodeLoc locMiddle = middlePoint.next(); - if (!middlePoint.hasNext()) - { - break; - } - - final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); - if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) - { - middlePoint.remove(); - } - else - { - currentX = locMiddle.getX(); - currentY = locMiddle.getY(); - currentZ = locMiddle.getZ(); - } - } - - return path; - } - - /** - * Convert geodata position to pathnode position - * @param geo_pos - * @return pathnode position - */ - public short getNodePos(int geo_pos) - { - return (short) (geo_pos >> 3); // OK? - } - - /** - * Convert node position to pathnode block position - * @param node_pos - * @return pathnode block position (0...255) - */ - public short getNodeBlock(int node_pos) - { - return (short) (node_pos % 256); - } - - public byte getRegionX(int node_pos) - { - return (byte) ((node_pos >> 8) + World.TILE_X_MIN); - } - - public byte getRegionY(int node_pos) - { - return (byte) ((node_pos >> 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 node_x rx - * @return - */ - public int calculateWorldX(short node_x) - { - return World.MAP_MIN_X + (node_x * 128) + 48; - } - - /** - * Convert pathnode y to World y position - * @param node_y - * @return - */ - public int calculateWorldY(short node_y) - { - return World.MAP_MIN_Y + (node_y * 128) + 48; - } - - private List constructPath(AbstractNode nodeValue) - { - final LinkedList path = new LinkedList<>(); - int previousDirectionX = Integer.MIN_VALUE; - int previousDirectionY = Integer.MIN_VALUE; - int directionX, directionY; - - AbstractNode node = nodeValue; - while (node.getParent() != null) - { - directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); - directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); - - // only add a new route point if moving direction changes - if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) - { - previousDirectionX = directionX; - previousDirectionY = directionY; - - path.addFirst(node.getLoc()); - node.setLoc(null); - } - - node = node.getParent(); - } - - return path; - } - - private CellNodeBuffer alloc(int size) - { - CellNodeBuffer current = null; - for (BufferInfo i : _buffers) - { - if (i.mapSize >= size) - { - for (CellNodeBuffer buf : i.buffer) - { - if (buf.lock()) - { - current = buf; - break; - } - } - if (current != null) - { - break; - } - - // not found, allocate temporary buffer - current = new CellNodeBuffer(i.mapSize); - current.lock(); - if (i.buffer.size() < i.count) - { - i.buffer.add(current); - break; - } - } - } - - return current; - } - - private static final class BufferInfo - { - final int mapSize; - final int count; - ArrayList buffer; - - public BufferInfo(int size, int cnt) - { - mapSize = size; - count = cnt; - buffer = new ArrayList<>(count); - } - } - - public static GeoEnginePathfinding getInstance() - { - return SingletonHolder.INSTANCE; - } - - private static class SingletonHolder - { - protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); - } -} diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java new file mode 100644 index 0000000000..49ec35aa1c --- /dev/null +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java @@ -0,0 +1,85 @@ +/* + * 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 . + */ +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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @param index : Index of the cell. + * @return short : Cell geodata Z coordinate, below given coordinates. + */ + public abstract byte getNswe(int index); +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java new file mode 100644 index 0000000000..f5997bf287 --- /dev/null +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java @@ -0,0 +1,130 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java new file mode 100644 index 0000000000..9af9d2a28a --- /dev/null +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java @@ -0,0 +1,95 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java new file mode 100644 index 0000000000..31fbf5d477 --- /dev/null +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java new file mode 100644 index 0000000000..f77d21d84b --- /dev/null +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java @@ -0,0 +1,68 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +public class BlockNull extends ABlock +{ + @Override + public boolean hasGeoPos() + { + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java deleted file mode 100644 index 61ea4fcf82..0000000000 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java +++ /dev/null @@ -1,48 +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 . - */ -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() - { - } -} diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java deleted file mode 100644 index 3c8de1a7f7..0000000000 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java +++ /dev/null @@ -1,77 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java deleted file mode 100644 index 1ccdefe3da..0000000000 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java +++ /dev/null @@ -1,56 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java new file mode 100644 index 0000000000..691b164e0c --- /dev/null +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ +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_ALL = 0x0F; + + // 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; +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java deleted file mode 100644 index 25eafc5a04..0000000000 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java +++ /dev/null @@ -1,42 +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 . - */ -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); -} diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java deleted file mode 100644 index 0d2c765002..0000000000 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java +++ /dev/null @@ -1,47 +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 . - */ -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(); -} diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java deleted file mode 100644 index 52df457360..0000000000 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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); - - // 1 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; - } -} diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java deleted file mode 100644 index 72a5a635c7..0000000000 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java +++ /dev/null @@ -1,55 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author HorridoJoho - */ -public final class NullRegion implements IRegion -{ - public static final NullRegion INSTANCE = new NullRegion(); - - @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 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; - } -} diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java deleted file mode 100644 index fc924cb875..0000000000 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java +++ /dev/null @@ -1,92 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java deleted file mode 100644 index 5087513ed9..0000000000 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java +++ /dev/null @@ -1,87 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public abstract class AbstractNode -{ - private T _loc; - private AbstractNode _parent; - - public AbstractNode(T loc) - { - _loc = loc; - } - - public void setParent(AbstractNode p) - { - _parent = p; - } - - public AbstractNode getParent() - { - return _parent; - } - - public T getLoc() - { - return _loc; - } - - public void setLoc(T l) - { - _loc = l; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); - return result; - } - - @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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java deleted file mode 100644 index 7223b5b2be..0000000000 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java +++ /dev/null @@ -1,33 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -/** - * @author -Nemesiss- - */ -public abstract class AbstractNodeLoc -{ - public abstract int getX(); - - public abstract int getY(); - - public abstract int getZ(); - - public abstract int getNodeX(); - - public abstract int getNodeY(); -} diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java deleted file mode 100644 index 3425234e0e..0000000000 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public class CellNode extends AbstractNode -{ - 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java deleted file mode 100644 index 522610bb7c..0000000000 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java +++ /dev/null @@ -1,348 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -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 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) - { - _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(); - } - - public final List debugPath() - { - final List 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 void getNeighbors() - { - if (!_current.getLoc().canGoAll()) - { - 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); - } - - // SouthEast - if ((nodeE != null) && (nodeS != null)) - { - if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) - { - addNode(x + 1, y + 1, z, true); - } - } - - // SouthWest - if ((nodeS != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) - { - addNode(x - 1, y + 1, z, true); - } - } - - // NorthEast - if ((nodeN != null) && (nodeE != null)) - { - if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) - { - addNode(x + 1, y - 1, z, true); - } - } - - // NorthWest - if ((nodeN != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) - { - addNode(x - 1, y - 1, z, true); - } - } - } - - private 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(); - // reinit node if needed - if (result.getLoc() != null) - { - result.getLoc().set(x, y, z); - } - else - { - result.setLoc(new NodeLoc(x, y, z)); - } - } - - return result; - } - - private 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 boolean isHighWeight(int x, int y, int z) - { - final CellNode result = getNode(x, y, z); - if (result == null) - { - return true; - } - - if (!result.getLoc().canGoAll()) - { - return true; - } - if (Math.abs(result.getLoc().getZ() - z) > 16) - { - return true; - } - - return false; - } - - private 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java new file mode 100644 index 0000000000..fa5ca43c2f --- /dev/null +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -0,0 +1,111 @@ +/* + * 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 . + */ +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 geodata values. + private int _geoX; + private int _geoY; + private byte _nswe; + + // 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; + + _costG = 0; + _costH = 0; + _costF = 0; + + _parent = null; + } + + public void setGeo(int gx, int gy, int gz, byte nswe) + { + super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); + + _geoX = gx; + _geoY = gy; + _nswe = nswe; + } + + 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 int getCostF() + { + return _costF; + } + + public Node getParent() + { + return _parent; + } + + @Override + public int compareTo(Node o) + { + return _costF - o._costF; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java new file mode 100644 index 0000000000..b464212c96 --- /dev/null +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -0,0 +1,368 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.awt.Color; +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; +import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; + +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 _opened; + // Container holding Nodes already explored. + private final List _closed; + + // Target coordinates. + private int _gtx; + private int _gty; + private int _gtz; + + // Pathfinding statistics. + private long _timeStamp; + private long _lastElapsedTime; + + 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 findPath(int gox, int goy, int goz, int gtx, int gty, int gtz) + { + // Set start timestamp. + _timeStamp = System.currentTimeMillis(); + + // 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)); + _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 constructPath() + { + // Create result. + final LinkedList 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; + } + + /** + * Creates list of Nodes to show debug path. + * @param debug : The debug packet to add debug informations in. + */ + public void debugPath(ExServerPrimitive debug) + { + // Add all opened node as yellow points. + for (Node n : _opened) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); + } + + // Add all opened node as blue points. + for (Node n : _closed) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); + } + } + + 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; + + _lastElapsedTime = System.currentTimeMillis() - _timeStamp; + _lock.unlock(); + } + + public long getElapsedTime() + { + return _lastElapsedTime; + } + + /** + * Expand the current {@link Node} by exploring its neighbors (axially and diagonally). + */ + private void expand() + { + // Movement is blocked, skip. + final byte nswe = _current.getNSWE(); + if (nswe == 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; + + // Can move north, expand. + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + } + + // Can move south, expand. + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + } + + // Can move west, expand. + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move east, expand. + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move north-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move north-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + } + + /** + * 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 weight : The weight of movement to the new node. + * @return The nswe of the added node. Blank, if not added. + */ + private byte addNode(int gx, int gy, int gzValue, int weight) + { + // 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]; + + // Set node geodata coordinates. + node.setGeo(gx, gy, gz, nswe); + + // 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, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + _opened.add(node); + _bufferIndex++; + return nswe; + } + + /** + * Calculate cost H value, calculated using diagonal distance method.
+ * 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. + final int dx = Math.abs(gx - _gtx); + final int dy = Math.abs(gy - _gty); + final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + + // Get diagonal and axial differences to the target. + final int dd = Math.min(dx, dy); + final int da = Math.max(dx, dy) - dd; + + // Calculate the diagonal distance of the node to the target. + return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + } +} \ No newline at end of file diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java deleted file mode 100644 index e3bad04b25..0000000000 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; - -/** - * @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 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 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; - // return super.hashCode(); - } - - @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; - if (_x != other._x) - { - return false; - } - if (_y != other._y) - { - return false; - } - if (_goNorth != other._goNorth) - { - return false; - } - if (_goEast != other._goEast) - { - return false; - } - if (_goSouth != other._goSouth) - { - return false; - } - if (_goWest != other._goWest) - { - return false; - } - if (_geoHeight != other._geoHeight) - { - return false; - } - return true; - } -} diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java index 1faf1ae423..ed261765e1 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java @@ -93,15 +93,15 @@ public class ZoneManager implements IXmlReader private static final Map SETTINGS = new HashMap<>(); private static final int SHIFT_BY = 15; - private static final int OFFSET_X = Math.abs(World.MAP_MIN_X >> SHIFT_BY); - private static final int OFFSET_Y = Math.abs(World.MAP_MIN_Y >> SHIFT_BY); + private static final int OFFSET_X = Math.abs(World.WORLD_X_MIN >> SHIFT_BY); + private static final int OFFSET_Y = Math.abs(World.WORLD_Y_MIN >> SHIFT_BY); private final Map, ConcurrentHashMap> _classZones = new ConcurrentHashMap<>(); private final Map _spawnTerritories = new ConcurrentHashMap<>(); private final AtomicInteger _lastDynamicId = new AtomicInteger(300000); private List _debugItems; - private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.MAP_MAX_X >> SHIFT_BY) + OFFSET_X + 1][(World.MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y + 1]; + private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.WORLD_X_MAX >> SHIFT_BY) + OFFSET_X + 1][(World.WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y + 1]; /** * Instantiates a new zone manager. diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/Location.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/Location.java index 73689ab1a6..ca3b22a3f0 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/Location.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/Location.java @@ -16,30 +16,30 @@ */ package org.l2jmobius.gameserver.model; +import java.util.Objects; + +import org.l2jmobius.commons.util.Point2D; import org.l2jmobius.gameserver.model.interfaces.ILocational; import org.l2jmobius.gameserver.model.interfaces.IPositionable; /** - * Location data transfer object.
- * Contains coordinates data, heading and instance Id. - * @author Zoey76 + * A datatype used to retain a 3D (x/y/z/heading) point. It got the capability to be set and cleaned. */ -public class Location implements IPositionable +public class Location extends Point2D implements IPositionable { - protected int _x; - protected int _y; - protected int _z; - private int _heading; + protected volatile int _z; + protected volatile int _heading; public Location(int x, int y, int z) { - this(x, y, z, 0); + super(x, y); + _z = z; + _heading = 0; } public Location(int x, int y, int z, int heading) { - _x = x; - _y = y; + super(x, y); _z = z; _heading = heading; } @@ -51,9 +51,8 @@ public class Location implements IPositionable public Location(StatSet set) { - _x = set.getInt("x"); - _y = set.getInt("y"); - _z = set.getInt("z"); + super(set.getInt("x", 0), set.getInt("y", 0)); + _z = set.getInt("z", 0); _heading = set.getInt("heading", 0); } @@ -146,6 +145,25 @@ public class Location implements IPositionable _heading = loc.getHeading(); } + @Override + public void clean() + { + super.clean(); + _z = 0; + } + + @Override + public Location clone() + { + return new Location(_x, _y, _z); + } + + @Override + public int hashCode() + { + return (31 * super.hashCode()) + Objects.hash(_z); + } + @Override public boolean equals(Object obj) { diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/World.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/World.java index c1510e8c0e..0d796dd834 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/World.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/World.java @@ -66,19 +66,19 @@ public class World public static final int TILE_Y_MAX = 26; public static final int TILE_ZERO_COORD_X = 20; public static final int TILE_ZERO_COORD_Y = 18; - public static final int MAP_MIN_X = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; - public static final int MAP_MIN_Y = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; + public static final int WORLD_X_MIN = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; + public static final int WORLD_Y_MIN = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; - public static final int MAP_MAX_X = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; - public static final int MAP_MAX_Y = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; + public static final int WORLD_X_MAX = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; + public static final int WORLD_Y_MAX = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; /** Calculated offset used so top left region is 0,0 */ - public static final int OFFSET_X = Math.abs(MAP_MIN_X >> SHIFT_BY); - public static final int OFFSET_Y = Math.abs(MAP_MIN_Y >> SHIFT_BY); + public static final int OFFSET_X = Math.abs(WORLD_X_MIN >> SHIFT_BY); + public static final int OFFSET_Y = Math.abs(WORLD_Y_MIN >> SHIFT_BY); /** Number of regions. */ - private static final int REGIONS_X = (MAP_MAX_X >> SHIFT_BY) + OFFSET_X; - private static final int REGIONS_Y = (MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y; + private static final int REGIONS_X = (WORLD_X_MAX >> SHIFT_BY) + OFFSET_X; + private static final int REGIONS_Y = (WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y; /** Map containing all the players in game. */ private static final Map _allPlayers = new ConcurrentHashMap<>(); @@ -817,9 +817,6 @@ public class World return _memberInPartyNumber.get(); } - /** - * @return the current instance of World - */ public static World getInstance() { return SingletonHolder.INSTANCE; diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/WorldObject.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/WorldObject.java index 0e5536bbc0..62313ffea1 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/WorldObject.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/WorldObject.java @@ -188,23 +188,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif synchronized (this) { int spawnX = x; - if (spawnX > World.MAP_MAX_X) + if (spawnX > World.WORLD_X_MAX) { - spawnX = World.MAP_MAX_X - 5000; + spawnX = World.WORLD_X_MAX - 5000; } - if (spawnX < World.MAP_MIN_X) + if (spawnX < World.WORLD_X_MIN) { - spawnX = World.MAP_MIN_X + 5000; + spawnX = World.WORLD_X_MIN + 5000; } int spawnY = y; - if (spawnY > World.MAP_MAX_Y) + if (spawnY > World.WORLD_Y_MAX) { - spawnY = World.MAP_MAX_Y - 5000; + spawnY = World.WORLD_Y_MAX - 5000; } - if (spawnY < World.MAP_MIN_Y) + if (spawnY < World.WORLD_Y_MIN) { - spawnY = World.MAP_MIN_Y + 5000; + spawnY = World.WORLD_Y_MIN + 5000; } // Set the x,y,z position of the WorldObject. If flagged with _isSpawned, setXYZ will automatically update world region, so avoid that. @@ -518,23 +518,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif public void setXYZInvisible(int x, int y, int z) { int correctX = x; - if (correctX > World.MAP_MAX_X) + if (correctX > World.WORLD_X_MAX) { - correctX = World.MAP_MAX_X - 5000; + correctX = World.WORLD_X_MAX - 5000; } - if (correctX < World.MAP_MIN_X) + if (correctX < World.WORLD_X_MIN) { - correctX = World.MAP_MIN_X + 5000; + correctX = World.WORLD_X_MIN + 5000; } int correctY = y; - if (correctY > World.MAP_MAX_Y) + if (correctY > World.WORLD_Y_MAX) { - correctY = World.MAP_MAX_Y - 5000; + correctY = World.WORLD_Y_MAX - 5000; } - if (correctY < World.MAP_MIN_Y) + if (correctY < World.WORLD_Y_MIN) { - correctY = World.MAP_MIN_Y + 5000; + correctY = World.WORLD_Y_MIN + 5000; } setXYZ(correctX, correctY, z); diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/actor/Creature.java index fc29cca657..8593d97e36 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -65,8 +65,6 @@ 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.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -2585,7 +2583,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3406,8 +3404,8 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe final int originalX = x; final int originalY = y; final int originalZ = z; - final int gtx = (originalX - World.MAP_MIN_X) >> 4; - final int gty = (originalY - World.MAP_MIN_Y) >> 4; + final int gtx = (originalX - World.WORLD_X_MIN) >> 4; + final int gty = (originalY - World.WORLD_Y_MIN) >> 4; if (isOnGeodataPath()) { try @@ -3430,7 +3428,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe && !(((curZ - z) > 300) && (distance < 300))) // Prohibit correcting destination if character wants to fall. { // location different if destination wasn't reached (or just z coord is different) - final Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(curX, curY, curZ, x, y, z, getInstanceWorld()); + final Location destiny = GeoEngine.getInstance().getValidLocation(curX, curY, curZ, x, y, z, getInstanceWorld()); x = destiny.getX(); y = destiny.getY(); z = destiny.getZ(); @@ -3444,7 +3442,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { if (isPlayer() && !_isFlying && !isInWater) diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/actor/Summon.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/actor/Summon.java index 2aaa8064dd..4b09eca1d7 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/actor/Summon.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/actor/Summon.java @@ -106,7 +106,7 @@ public abstract class Summon extends Playable final int x = owner.getX(); final int y = owner.getY(); final int z = owner.getZ(); - final Location location = GeoEngine.getInstance().canMoveToTargetLoc(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, owner.getInstanceWorld()); + final Location location = GeoEngine.getInstance().getValidLocation(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, getInstanceWorld()); setXYZInvisible(location.getX(), location.getY(), location.getZ()); } diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java index ee59cd90c7..0cc4dfeff8 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java @@ -1499,7 +1499,7 @@ public class ItemInstance extends WorldObject if (dropper != null) { final Instance instance = dropper.getInstanceWorld(); - final Location dropDest = GeoEngine.getInstance().canMoveToTargetLoc(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); + final Location dropDest = GeoEngine.getInstance().getValidLocation(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); x = dropDest.getX(); y = dropDest.getY(); z = dropDest.getZ(); diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java index 5d294fe47c..558bf92edf 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java @@ -1179,7 +1179,7 @@ public class SkillCaster implements Runnable } } - final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().canMoveToTargetLoc(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); + final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().getValidLocation(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); creature.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); creature.broadcastPacket(new FlyToLocation(creature, destination, flyType, 0, 0, 333)); diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java index c62c821f44..30068134e3 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java @@ -114,7 +114,7 @@ public class ValidatePosition implements IClientIncomingPacket { if (player.isFalling(_z)) { - final int nearestZ = GeoEngine.getInstance().getHigherHeight(_x, _y, _z); + final int nearestZ = GeoEngine.getInstance().getHeight(_x, _y, _z); if (player.getZ() < nearestZ) { player.setXYZ(_x, _y, nearestZ); diff --git a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/util/GeoUtils.java index 4600a0fc53..19a979819e 100644 --- a/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_Classic_Interlude/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -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.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; @@ -30,21 +30,21 @@ public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getInstance().getWorldX(iter.x()); - final int wy = GeoEngine.getInstance().getWorldY(iter.y()); + final int wx = GeoEngine.getWorldX(iter.x()); + final int wy = GeoEngine.getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public final class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); - int wy = GeoEngine.getInstance().getWorldY(prevY); + int wx = GeoEngine.getWorldX(prevX); + int wy = GeoEngine.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.getInstance().getWorldX(curX); - wy = GeoEngine.getInstance().getWorldY(curY); + wx = GeoEngine.getWorldX(curX); + wy = GeoEngine.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().checkNearestNswe(x, y, z, nswe)) + if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0) { return Color.GREEN; } @@ -109,9 +109,8 @@ public final class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final GeoEngine ge = GeoEngine.getInstance(); - final int playerGx = ge.getGeoX(player.getX()); - final int playerGy = ge.getGeoY(player.getY()); + final int playerGx = GeoEngine.getGeoX(player.getX()); + final int playerGy = GeoEngine.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -135,32 +134,32 @@ public final class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = ge.getWorldX(gx); - final int y = ge.getWorldY(gy); - final int z = ge.getNearestZ(gx, gy, player.getZ()); + final int x = GeoEngine.getWorldX(gx); + final int y = GeoEngine.getWorldY(gy); + final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); + Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); 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, Cell.NSWE_EAST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); 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, Cell.NSWE_SOUTH); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); 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, Cell.NSWE_WEST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); 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); @@ -188,41 +187,41 @@ public final class GeoUtils { if (y > lastY) { - return Cell.NSWE_SOUTH_EAST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E; } else if (y < lastY) { - return Cell.NSWE_NORTH_EAST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E; } else { - return Cell.NSWE_EAST; + return GeoStructure.CELL_FLAG_E; } } else if (x < lastX) // west { if (y > lastY) { - return Cell.NSWE_SOUTH_WEST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W; } else if (y < lastY) { - return Cell.NSWE_NORTH_WEST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W; } else { - return Cell.NSWE_WEST; + return GeoStructure.CELL_FLAG_W; } } else // unchanged x { if (y > lastY) { - return Cell.NSWE_SOUTH; + return GeoStructure.CELL_FLAG_S; } else if (y < lastY) { - return Cell.NSWE_NORTH; + return GeoStructure.CELL_FLAG_N; } else { diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/config/GeoEngine.ini b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/config/GeoEngine.ini index 609e8a89a7..e740c678dd 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/config/GeoEngine.ini @@ -6,33 +6,44 @@ # at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ 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 + # ================================================================= -# Path finding +# Pathfinding # ================================================================= # 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 +# an alternative path (e.g. walk around obstacle), default: True +PathFinding = True -# Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 -PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 +# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 -# Weight for nodes without obstacles far from walls -LowWeight = 0.5 +# Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 +MoveWeight = 10 +MoveWeightDiag = 14 -# Weight for nodes near walls -MediumWeight = 2 +# 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 -# Weight for nodes with obstacles -HighWeight = 3 +# 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. -# Default: LowWeight * sqrt(2) -DiagonalWeight = 0.707 +# Maximum number of generated nodes per one path-finding process, default 3500 +MaxIterations = 3500 # ================================================================= -# Other +# Line of Sight # ================================================================= -# Correct player Z after falling through the ground. -CorrectPlayerZ = False +# 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 diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/geodata/L2D_GeoEngine.patch b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/geodata/L2D_GeoEngine.patch deleted file mode 100644 index f8577e3a0e..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/geodata/L2D_GeoEngine.patch +++ /dev/null @@ -1,6035 +0,0 @@ -Index: dist/game/GeoDataConverter.bat -=================================================================== ---- dist/game/GeoDataConverter.bat (nonexistent) -+++ dist/game/GeoDataConverter.bat (working copy) -@@ -0,0 +1,6 @@ -+@echo off -+title L2D geodata converter -+ -+java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter -+ -+pause -Index: dist/game/GeoDataConverter.sh -=================================================================== ---- dist/game/GeoDataConverter.sh (nonexistent) -+++ dist/game/GeoDataConverter.sh (working copy) -@@ -0,0 +1,4 @@ -+#! /bin/sh -+ -+java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 -+ -Index: dist/game/config/GeoEngine.ini -=================================================================== ---- dist/game/config/GeoEngine.ini (revision 8409) -+++ dist/game/config/GeoEngine.ini (working copy) -@@ -1,6 +1,10 @@ - # ================================================================= - # Geodata - # ================================================================= -+# Because of real-time performance we are using geodata files only in -+# diagonal L2D format now (using filename e.g. 22_16.l2d). -+# L2D geodata can be obtained by conversion of existing L2J or L2OFF geodata. -+# Launch "GeoDataConverter.bat/sh" and follow instructions to start the conversion. - - # 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/ -@@ -13,6 +17,16 @@ - CoordSynchronize = 2 - - # ================================================================= -+# Path checking -+# ================================================================= -+ -+# 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 finding - # ================================================================= - -@@ -23,19 +37,23 @@ - # Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - --# Weight for nodes without obstacles far from walls --LowWeight = 0.5 -+# Base path weight, when moving from one node to another on axis direction, default: 10 -+BaseWeight = 10 - --# Weight for nodes near walls --MediumWeight = 2 -+# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -+DiagonalWeight = 14 - --# Weight for nodes with obstacles --HighWeight = 3 -+# When movement flags of target node is blocked to any direction, multiply movement weight by this multiplier. -+# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 10 -+ObstacleMultiplier = 10 - --# Weight for diagonal movement. --# Default: LowWeight * sqrt(2) --DiagonalWeight = 0.707 -+# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 20 -+# For proper function must be higher than BaseWeight and/or DiagonalWeight. -+HeuristicWeight = 20 - -+# Maximum number of generated nodes per one path-finding process, default 3500 -+MaxIterations = 3500 -+ - # ================================================================= - # Other - # ================================================================= -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (working copy) -@@ -55,9 +55,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -@@ -73,9 +72,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (working copy) -@@ -18,11 +18,11 @@ - - import java.util.List; - --import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -+import org.l2jmobius.gameserver.geoengine.GeoEngine; - import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -+import org.l2jmobius.gameserver.network.SystemMessageId; - import org.l2jmobius.gameserver.util.BuilderUtil; - - public class AdminPathNode implements IAdminCommandHandler -@@ -37,29 +37,30 @@ - { - if (command.equals("admin_path_find")) - { -- if (!Config.PATHFINDING) -- { -- BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); -- return true; -- } - if (activeChar.getTarget() != null) - { -- final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); -+ final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); - if (path == null) - { -- BuilderUtil.sendSysMessage(activeChar, "No Route!"); -- return true; -+ BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); - } -- for (AbstractNodeLoc a : path) -+ else - { -- BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); -+ for (Location point : path) -+ { -+ BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); -+ } - } - } - else - { -- BuilderUtil.sendSysMessage(activeChar, "No Target!"); -+ activeChar.sendPacket(SystemMessageId.INVALID_TARGET); - } - } -+ else -+ { -+ return false; -+ } - return true; - } - -Index: java/org/l2jmobius/Config.java -=================================================================== ---- java/org/l2jmobius/Config.java (revision 8409) -+++ java/org/l2jmobius/Config.java (working copy) -@@ -32,7 +32,6 @@ - import java.net.UnknownHostException; - import java.nio.charset.StandardCharsets; - import java.nio.file.Files; --import java.nio.file.Path; - import java.nio.file.Paths; - import java.time.Duration; - import java.util.ArrayList; -@@ -966,14 +965,17 @@ - // -------------------------------------------------- - // GeoEngine - // -------------------------------------------------- -- public static Path GEODATA_PATH; -+ public static String GEODATA_PATH; -+ public static int COORD_SYNCHRONIZE; -+ public static int PART_OF_CHARACTER_HEIGHT; -+ public static int MAX_OBSTACLE_HEIGHT; - public static boolean PATHFINDING; - public static String PATHFIND_BUFFERS; -- public static float LOW_WEIGHT; -- public static float MEDIUM_WEIGHT; -- public static float HIGH_WEIGHT; -- public static float DIAGONAL_WEIGHT; -- public static int COORD_SYNCHRONIZE; -+ public static int BASE_WEIGHT; -+ public static int DIAGONAL_WEIGHT; -+ public static int HEURISTIC_WEIGHT; -+ public static int OBSTACLE_MULTIPLIER; -+ public static int MAX_ITERATIONS; - public static boolean CORRECT_PLAYER_Z; - - /** Attribute System */ -@@ -2568,14 +2570,17 @@ - - // Load GeoEngine config file (if exists) - final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); -- GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); -+ GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); -+ COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); -+ MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); - PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -- LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); -- MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); -- HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); -- DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); -- COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); -+ DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); -+ OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); -+ HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); -+ MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); - CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); - - // Load AllowedPlayerRaces config file (if exists) -Index: java/org/l2jmobius/gameserver/geoengine/GeoEngine.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (revision 8435) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (working copy) -@@ -16,100 +16,90 @@ - */ - package org.l2jmobius.gameserver.geoengine; - --import java.io.IOException; -+import java.io.File; - import java.io.RandomAccessFile; - import java.nio.ByteOrder; --import java.nio.channels.FileChannel.MapMode; --import java.nio.file.Files; --import java.nio.file.Path; --import java.util.concurrent.atomic.AtomicReferenceArray; --import java.util.logging.Level; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.List; - import java.util.logging.Logger; - - import org.l2jmobius.Config; - import org.l2jmobius.gameserver.data.xml.DoorData; - import org.l2jmobius.gameserver.data.xml.FenceData; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; --import org.l2jmobius.gameserver.geoengine.geodata.IRegion; --import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; --import org.l2jmobius.gameserver.geoengine.geodata.Region; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.World; - import org.l2jmobius.gameserver.model.WorldObject; -+import org.l2jmobius.gameserver.model.actor.Creature; - import org.l2jmobius.gameserver.model.instancezone.Instance; --import org.l2jmobius.gameserver.model.interfaces.ILocational; --import org.l2jmobius.gameserver.util.GeoUtils; --import org.l2jmobius.gameserver.util.LinePointIterator; --import org.l2jmobius.gameserver.util.LinePointIterator3D; -+import org.l2jmobius.gameserver.util.MathUtil; - - /** -- * @author -Nemesiss-, HorridoJoho -+ * @author Hasha - */ - public class GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); -+ protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - -- private static final int WORLD_MIN_X = -655360; -- private static final int WORLD_MIN_Y = -589824; -- private static final int WORLD_MIN_Z = -16384; -+ private final ABlock[][] _blocks; -+ private final BlockNull _nullBlock; - -- /** 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; -- -- /** The regions array */ -- private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); -- -- private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; -- private static final int ELEVATED_SEE_OVER_DISTANCE = 2; -- private static final int MAX_SEE_OVER_HEIGHT = 48; -- -- protected GeoEngine() -+ /** -+ * GeoEngine constructor. Loads all geodata files of chosen geodata format. -+ */ -+ public GeoEngine() - { - LOGGER.info("GeoEngine: Initializing..."); -- for (int i = 0; i < _regions.length(); i++) -- { -- _regions.set(i, NullRegion.INSTANCE); -- } - -+ // initialize block container -+ _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; -+ -+ // load null block -+ _nullBlock = new BlockNull(); -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files according to geoengine config setup - int loaded = 0; -- try -+ long fileSize = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { -- for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { -- for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) -+ final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); -+ if (f.exists() && !f.isDirectory()) - { -- final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); -- if (Files.exists(geoFilePath)) -+ // region file is load-able, try to load it -+ if (loadGeoBlocks(rx, ry)) - { -- try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) -- { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -- loaded++; -- } -+ loaded++; -+ fileSize += f.length(); - } - } -+ else -+ { -+ // region file is not load-able, load null blocks -+ loadNullBlocks(rx, ry); -+ } - } - } -- catch (Exception e) -+ -+ LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -+ if (loaded > 0) - { -- LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); -- System.exit(1); -+ LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); - } - -- LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -- -- // Avoid wrong configs when no files are loaded. -+ // avoid wrong configs when no files are loaded - if (loaded == 0) - { - if (Config.PATHFINDING) -@@ -123,619 +113,820 @@ - LOGGER.info("GeoEngine: Forcing CoordSynchronize setting to -1."); - } - } -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); - } - - /** -- * @param geoX -- * @param geoY -- * @return the region -+ * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. -+ * @return boolean : True, when geodata file was loaded without problem. - */ -- private IRegion getRegion(int geoX, int geoY) -+ private final boolean loadGeoBlocks(int regionX, int regionY) - { -- final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); -- if ((region < 0) || (region >= _regions.length())) -+ final String filename = String.format(GeoFormat.L2D.getFilename(), regionX, regionY); -+ -+ // standard load -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) - { -- return null; -+ // initialize file buffer -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, GeoFormat.L2D); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ } -+ -+ // check data consistency -+ if (buffer.remaining() > 0) -+ { -+ LOGGER.warning("GeoEngine: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ } -+ -+ // loading was successful -+ return true; - } -- return _regions.get(region); -- } -- -- /** -- * @param filePath -- * @param regionX -- * @param regionY -- * @throws IOException -- */ -- 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")) -+ catch (Exception e) - { -- _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -+ // an error occured while loading, load null blocks -+ LOGGER.warning("GeoEngine: Error while loading " + filename + " region file."); -+ LOGGER.warning(e.getMessage()); -+ -+ // replace whole region file with null blocks -+ loadNullBlocks(regionX, regionY); -+ -+ // loading was not successful -+ return false; - } - } - - /** -- * @param regionX -- * @param regionY -+ * Loads null blocks. Used when no region file is detected or an error occurs during loading. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. - */ -- public void unloadRegion(int regionX, int regionY) -+ private final void loadNullBlocks(int regionX, int regionY) - { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); -- } -- -- /** -- * @param geoX -- * @param geoY -- * @return if geodata exist -- */ -- public boolean hasGeoPos(int geoX, int geoY) -- { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // load all null blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { -- return false; -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[blockX + ix][blockY + iy] = _nullBlock; -+ } - } -- return region.hasGeo(); - } - -+ // GEODATA - GENERAL -+ - /** -- * Checks the specified position for available geodata. -- * @param x the world x -- * @param y the world y -- * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise -+ * Converts world X to geodata X. -+ * @param worldX -+ * @return int : Geo X - */ -- public boolean hasGeo(int x, int y) -+ public static int getGeoX(int worldX) - { -- return hasGeoPos(getGeoX(x), getGeoY(y)); -+ return (MathUtil.limit(worldX, World.MAP_MIN_X, World.MAP_MAX_X) - World.MAP_MIN_X) >> 4; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe check -+ * Converts world Y to geodata Y. -+ * @param worldY -+ * @return int : Geo Y - */ -- public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) -+ public static int getGeoY(int worldY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return true; -- } -- return region.checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(worldY, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.MAP_MIN_Y) >> 4; - } - - /** -+ * Converts geodata X to world X. - * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe anti-corner cut -+ * @return int : World X - */ -- public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) -+ public static int getWorldX(int geoX) - { -- boolean can = true; -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- return can && checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(geoX, 0, GeoStructure.GEO_CELLS_X) << 4) + World.MAP_MIN_X + 8; - } - - /** -- * @param geoX -+ * Converts geodata Y to world Y. - * @param geoY -- * @param worldZ -- * @return the nearest Z value -+ * @return int : World Y - */ -- public int getNearestZ(int geoX, int geoY, int worldZ) -+ public static int getWorldY(int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return worldZ; -- } -- return region.getNearestZ(geoX, geoY, worldZ); -+ return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next lower Z value -+ * Returns block of geodata on given coordinates. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return {@link ABlock} : Block of geodata. - */ -- public int getNextLowerZ(int geoX, int geoY, int worldZ) -+ private final ABlock getBlock(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final int x = geoX / GeoStructure.BLOCK_CELLS_X; -+ final int y = geoY / GeoStructure.BLOCK_CELLS_Y; -+ -+ // if x or y is out of array return null -+ if ((x > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) - { -- return worldZ; -+ return _blocks[x][y]; - } -- return region.getNextLowerZ(geoX, geoY, worldZ); -+ return null; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next higher Z value -+ * Check if geo coordinates has geo. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return boolean : True, if given geo coordinates have geodata - */ -- public int getNextHigherZ(int geoX, int geoY, int worldZ) -+ public boolean hasGeoPos(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final ABlock block = getBlock(geoX, geoY); -+ if (block == null) // null block check - { -- return worldZ; -+ // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) -+ // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); -+ return false; - } -- return region.getNextHigherZ(geoX, geoY, worldZ); -+ return block.hasGeoPos(); - } - - /** -- * Gets the Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the height of cell, which is closest to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, closest to given coordinates. - */ -- public int getHeight(int x, int y, int z) -+ public short getHeightNearest(int geoX, int geoY, int worldZ) - { -- return getNearestZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getHeightNearest(geoX, geoY, worldZ) : (short) worldZ; - } - - /** -- * Gets the next lower Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the NSWE flag byte of cell, which is closes to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. - */ -- public int getLowerHeight(int x, int y, int z) -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) - { -- return getNextLowerZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getNsweNearest(geoX, geoY, worldZ) : (byte) 0xFF; - } - - /** -- * Gets the next higher Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Check if world coordinates has geo. -+ * @param worldX : World X -+ * @param worldY : World Y -+ * @return boolean : True, if given world coordinates have geodata - */ -- public int getHigherHeight(int x, int y, int z) -+ public boolean hasGeo(int worldX, int worldY) - { -- return getNextHigherZ(getGeoX(x), getGeoY(y), z); -+ return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); - } - - /** -- * @param worldX -- * @return the geo X -+ * Returns closest Z coordinate according to geodata. -+ * @param worldX : world x -+ * @param worldY : world y -+ * @param worldZ : world z -+ * @return short : nearest Z coordinates according to geodata - */ -- public int getGeoX(int worldX) -+ public short getHeight(int worldX, int worldY, int worldZ) - { -- return (worldX - WORLD_MIN_X) / 16; -+ return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - -- /** -- * @param worldY -- * @return the geo Y -- */ -- public int getGeoY(int worldY) -- { -- return (worldY - WORLD_MIN_Y) / 16; -- } -+ // PATHFINDING - - /** -- * @param worldZ -- * @return the geo Z -+ * Check line of sight from {@link WorldObject} to {@link WorldObject}. -+ * @param origin : The origin object. -+ * @param target : The target object. -+ * @return {@code boolean} : True if origin can see target - */ -- public int getGeoZ(int worldZ) -+ public boolean canSeeTarget(WorldObject origin, WorldObject target) - { -- return (worldZ - WORLD_MIN_Z) / 16; -- } -- -- /** -- * @param geoX -- * @return the world X -- */ -- public int getWorldX(int geoX) -- { -- return (geoX * 16) + WORLD_MIN_X + 8; -- } -- -- /** -- * @param geoY -- * @return the world Y -- */ -- public int getWorldY(int geoY) -- { -- return (geoY * 16) + WORLD_MIN_Y + 8; -- } -- -- /** -- * @param geoZ -- * @return the world Z -- */ -- public int getWorldZ(int geoZ) -- { -- return (geoZ * 16) + WORLD_MIN_Z + 8; -- } -- -- /** -- * Can see target. Doors as target always return true. Checks doors between. -- * @param cha the character -- * @param target the target -- * @return {@code true} if the character can see the target (LOS), {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, WorldObject target) -- { -- if (target.isDoor()) -+ if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) - { -- // Can always see doors. - return true; - } -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param cha the character -- * @param worldPosition the world position -- * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) -- { -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param world -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @param tworld the target's instanceId -- * @return -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) -- { -- return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instance -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) -- { -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) -+ -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = target.getX(); -+ final int ty = target.getY(); -+ final int tz = target.getZ(); -+ -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } -- return canSeeTarget(x, y, z, tx, ty, tz); -- } -- -- /** -- * @param prevX -- * @param prevY -- * @param prevGeoZ -- * @param curX -- * @param curY -- * @param nswe -- * @return the LOS Z value -- */ -- private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) -- { -- if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { -- throw new RuntimeException("Multiple directions!"); -+ return false; - } - -- if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- return getNearestZ(curX, curY, prevGeoZ); -+ return true; - } - -- return getNextHigherZ(curX, curY, prevGeoZ); -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) -+ { -+ return true; -+ } -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) -+ { -+ return goz == gtz; -+ } -+ -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) -+ { -+ oheight = ((Creature) origin).getCollisionHeight() * 2; -+ } -+ -+ double theight = 0; -+ if (target.isCreature()) -+ { -+ theight = ((Creature) target).getCollisionHeight() * 2; -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); - } - - /** -- * Can see target. Does not check doors between. -- * @param xValue the x coordinate -- * @param yValue the y coordinate -- * @param zValue the z coordinate -- * @param txValue the target's x coordinate -- * @param tyValue the target's y coordinate -- * @param tzValue the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -+ * Check line of sight from {@link WorldObject} to {@link Location}. -+ * @param origin : The origin object. -+ * @param position : The target position. -+ * @return {@code boolean} : True if object can see position - */ -- public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) -+ public boolean canSeeTarget(WorldObject origin, Location position) - { -- int x = xValue; -- int y = yValue; -- int tx = txValue; -- int ty = tyValue; -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = position.getX(); -+ final int ty = position.getY(); -+ final int tz = position.getZ(); - -- int geoX = getGeoX(x); -- int geoY = getGeoY(y); -- int tGeoX = getGeoX(tx); -- int tGeoY = getGeoY(ty); -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) -+ { -+ return false; -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) -+ { -+ return false; -+ } - -- int z = getNearestZ(geoX, geoY, zValue); -- int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- // Fastpath. -- if ((geoX == tGeoX) && (geoY == tGeoY)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- if (hasGeoPos(tGeoX, tGeoY)) -- { -- return z == tz; -- } - return true; - } - -- if (tz > z) -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) - { -- int tmp = tx; -- tx = x; -- x = tmp; -- -- tmp = ty; -- ty = y; -- y = tmp; -- -- tmp = tz; -- tz = z; -- z = tmp; -- -- tmp = tGeoX; -- tGeoX = geoX; -- geoX = tmp; -- -- tmp = tGeoY; -- tGeoY = geoY; -- geoY = tmp; -+ return goz == gtz; - } - -- final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); -- // First point is guaranteed to be available, skip it, we can always see our own position. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- final int prevZ = pointIter.z(); -- int prevGeoZ = prevZ; -- int ptIndex = 0; -- while (pointIter.next()) -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight(); -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); -+ } -+ -+ /** -+ * Simple check for origin to target visibility. -+ * @param goxValue : origin X geodata coordinate -+ * @param goyValue : origin Y geodata coordinate -+ * @param gozValue : origin Z geodata coordinate -+ * @param oheight : origin height (if instance of {@link Character}) -+ * @param gtxValue : target X geodata coordinate -+ * @param gtyValue : target Y geodata coordinate -+ * @param gtzValue : target Z geodata coordinate -+ * @param theight : target height (if instance of {@link Character}) -+ * @param instance -+ * @return {@code boolean} : True, when target can be seen. -+ */ -+ private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) -+ { -+ int goz = gozValue; -+ int gtz = gtzValue; -+ int gox = goxValue; -+ int goy = goyValue; -+ int gtx = gtxValue; -+ int gty = gtyValue; -+ -+ // get line of sight Z coordinates -+ double losoz = goz + ((oheight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ double lostz = gtz + ((theight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ -+ // get X delta and signum -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirox = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; -+ final byte dirtx = sx > 0 ? GeoStructure.CELL_FLAG_W : GeoStructure.CELL_FLAG_E; -+ -+ // get Y delta and signum -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte diroy = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ final byte dirty = sy > 0 ? GeoStructure.CELL_FLAG_N : GeoStructure.CELL_FLAG_S; -+ -+ // get Z delta -+ final int dm = Math.max(dx, dy); -+ final double dz = (lostz - losoz) / dm; -+ -+ // get direction flag for diagonal movement -+ final byte diroxy = getDirXY(dirox, diroy); -+ final byte dirtxy = getDirXY(dirtx, dirty); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte diro; -+ byte dirt; -+ -+ // initialize node values -+ int nox = gox; -+ int noy = goy; -+ int ntx = gtx; -+ int nty = gty; -+ byte nsweo = getNsweNearest(gox, goy, goz); -+ byte nswet = getNsweNearest(gtx, gty, gtz); -+ -+ // loop -+ ABlock block; -+ int index; -+ for (int i = 0; i < ((dm + 1) / 2); i++) -+ { -+ // reset direction flag -+ diro = 0; -+ dirt = 0; - -- if ((curX == prevX) && (curY == prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- continue; -+ // calculate next point XY coordinates -+ d -= dy; -+ d += dx; -+ nox += sx; -+ ntx -= sx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroxy; -+ dirt |= dirtxy; - } -+ else if (e2 > -dy) -+ { -+ // calculate next point X coordinate -+ d -= dy; -+ nox += sx; -+ ntx -= sx; -+ diro |= dirox; -+ dirt |= dirtx; -+ } -+ else if (e2 < dx) -+ { -+ // calculate next point Y coordinate -+ d += dx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroy; -+ dirt |= dirty; -+ } - -- final int beeCurZ = pointIter.z(); -- int curGeoZ = prevGeoZ; -- -- // Check if the position has geodata. -- if (hasGeoPos(curX, curY)) - { -- final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); -- curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); -- int maxHeight; -- if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) -+ // get block of the next cell -+ block = getBlock(nox, noy); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nsweo & diro) == 0) - { -- maxHeight = z + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); - } - else - { -- maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); - } - -- boolean canSeeThrough = false; -- if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) -+ // layer does not exist, return -+ if (index == -1) - { -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else -- { -- canSeeThrough = true; -- } -+ return false; - } - -- if (!canSeeThrough) -+ // get layer and next line of sight Z coordinate -+ goz = block.getHeight(index); -+ losoz += dz; -+ -+ // perform line of sight check, return when fails -+ if ((goz - losoz) > Config.MAX_OBSTACLE_HEIGHT) - { - return false; - } -+ -+ // get layer nswe -+ nsweo = block.getNswe(index); - } -+ { -+ // get block of the next cell -+ block = getBlock(ntx, nty); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nswet & dirt) == 0) -+ { -+ index = block.getIndexAbove(ntx, nty, gtz - GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ else -+ { -+ index = block.getIndexBelow(ntx, nty, gtz + GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ -+ // layer does not exist, return -+ if (index == -1) -+ { -+ return false; -+ } -+ -+ // get layer and next line of sight Z coordinate -+ gtz = block.getHeight(index); -+ lostz -= dz; -+ -+ // perform line of sight check, return when fails -+ if ((gtz - lostz) > Config.MAX_OBSTACLE_HEIGHT) -+ { -+ return false; -+ } -+ -+ // get layer nswe -+ nswet = block.getNswe(index); -+ } - -- prevX = curX; -- prevY = curY; -- prevGeoZ = curGeoZ; -- ++ptIndex; -+ // update coords -+ gox = nox; -+ goy = noy; -+ gtx = ntx; -+ gty = nty; - } - -- return true; -+ // when iteration is completed, compare final Z coordinates -+ return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); - } - - /** -- * Move check. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param zValue the z coordinate -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tzValue the target's z coordinate -- * @param instance the instance -- * @return the last Location (x,y,z) where player can walk - just before wall -+ * Check movement from coordinates to coordinates. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {code boolean} : True if target coordinates are reachable from origin coordinates - */ -- public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) -+ public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- final int geoX = getGeoX(x); -- final int geoY = getGeoY(y); -- final int z = getNearestZ(geoX, geoY, zValue); -- final int tGeoX = getGeoX(tx); -- final int tGeoY = getGeoY(ty); -- final int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } -- if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = z; -+ // perform geodata check -+ final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); -+ return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); -+ } -+ -+ /** -+ * Check movement from origin to target. Returns last available point in the checked path. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {@link Location} : Last point where object can walk (just before wall) -+ */ -+ public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) -+ { -+ return new Location(ox, oy, oz); -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) -+ { -+ return new Location(ox, oy, oz); -+ } - -- while (pointIter.next()) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -- -- if (hasGeoPos(prevX, prevY)) -- { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -- { -- // Can't move, return previous location. -- return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); -- } -- } -- -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return new Location(tx, ty, tz); - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != tz)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- // Different floors, return start location. -- return new Location(x, y, z); -+ return new Location(tx, ty, tz); - } - -- return new Location(tx, ty, tz); -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) -+ { -+ return new Location(tx, ty, tz); -+ } -+ -+ // perform geodata check -+ return checkMove(gox, goy, goz, gtx, gty, gtz, instance); - } - - /** -- * Checks if its possible to move from one location to another. -- * @param fromX the X coordinate to start checking from -- * @param fromY the Y coordinate to start checking from -- * @param fromZvalue the Z coordinate to start checking from -- * @param toX the X coordinate to end checking at -- * @param toY the Y coordinate to end checking at -- * @param toZvalue the Z coordinate to end checking at -- * @param instance the instance -- * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise -+ * With this method you can check if a position is visible or can be reached by beeline movement.
-+ * Target X and Y reachable and Z is on same floor: -+ *
    -+ *
  • Location of the target with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y reachable but Z is on another floor: -+ *
    -+ *
  • Location of the origin with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y not reachable: -+ *
    -+ *
  • Last accessible location in destination to target.
  • -+ *
-+ * @param gox : origin X geodata coordinate -+ * @param goy : origin Y geodata coordinate -+ * @param goz : origin Z geodata coordinate -+ * @param gtx : target X geodata coordinate -+ * @param gty : target Y geodata coordinate -+ * @param gtz : target Z geodata coordinate -+ * @param instance -+ * @return {@link GeoLocation} : The last allowed point of movement. - */ -- public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) -+ protected final GeoLocation checkMove(int gox, int goy, int goz, int gtx, int gty, int gtz, Instance instance) - { -- final int geoX = getGeoX(fromX); -- final int geoY = getGeoY(fromY); -- final int fromZ = getNearestZ(geoX, geoY, fromZvalue); -- final int tGeoX = getGeoX(toX); -- final int tGeoY = getGeoY(toY); -- final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); -- -- if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) -+ if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } -- if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) -+ if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = fromZ; -+ // get X delta, signum and direction flag -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirX = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; - -- while (pointIter.next()) -+ // get Y delta, signum and direction flag -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte dirY = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ -+ // get direction flag for diagonal movement -+ final byte dirXY = getDirXY(dirX, dirY); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte direction; -+ -+ // load pointer coordinates -+ int gpx = gox; -+ int gpy = goy; -+ int gpz = goz; -+ -+ // load next pointer -+ int nx = gpx; -+ int ny = gpy; -+ -+ // loop -+ int count = 0; -+ while (count++ < Config.MAX_ITERATIONS) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -+ direction = 0; - -- if (hasGeoPos(prevX, prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -+ d -= dy; -+ d += dx; -+ nx += sx; -+ ny += sy; -+ direction |= dirXY; -+ } -+ else if (e2 > -dy) -+ { -+ d -= dy; -+ nx += sx; -+ direction |= dirX; -+ } -+ else if (e2 < dx) -+ { -+ d += dx; -+ ny += sy; -+ direction |= dirY; -+ } -+ -+ // obstacle found, return -+ if ((getNsweNearest(gpx, gpy, gpz) & direction) == 0) -+ { -+ return new GeoLocation(gpx, gpy, gpz); -+ } -+ -+ // update pointer coordinates -+ gpx = nx; -+ gpy = ny; -+ gpz = getHeightNearest(nx, ny, gpz); -+ -+ // target coordinates reached -+ if ((gpx == gtx) && (gpy == gty)) -+ { -+ if (gpz == gtz) - { -- return false; -+ // path found, Z coordinates are okay, return target point -+ return new GeoLocation(gtx, gty, gtz); - } -+ -+ // path found, Z coordinates are not okay, return last good point -+ return new GeoLocation(gpx, gpy, gpz); - } -+ } -+ -+ return new GeoLocation(gox, goy, goz); -+ } -+ -+ /** -+ * Returns diagonal NSWE flag format of combined two NSWE flags. -+ * @param dirX : X direction NSWE flag -+ * @param dirY : Y direction NSWE flag -+ * @return byte : NSWE flag of combined direction -+ */ -+ private static byte getDirXY(byte dirX, byte dirY) -+ { -+ // check axis directions -+ if (dirY == GeoStructure.CELL_FLAG_N) -+ { -+ if (dirX == GeoStructure.CELL_FLAG_W) -+ { -+ return GeoStructure.CELL_FLAG_NW; -+ } - -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return GeoStructure.CELL_FLAG_NE; - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) -+ if (dirX == GeoStructure.CELL_FLAG_W) - { -- // Different floors. -- return false; -+ return GeoStructure.CELL_FLAG_SW; - } - -- return true; -+ return GeoStructure.CELL_FLAG_SE; - } - -+ /** -+ * Returns the list of location objects as a result of complete path calculation. -+ * @param ox : origin x -+ * @param oy : origin y -+ * @param oz : origin z -+ * @param tx : target x -+ * @param ty : target y -+ * @param tz : target z -+ * @param instance -+ * @return {@code List} : complete path from nodes -+ */ -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ return null; -+ } -+ -+ /** -+ * Returns the instance of the {@link GeoEngine}. -+ * @return {@link GeoEngine} : The instance. -+ */ - public static GeoEngine getInstance() - { - return SingletonHolder.INSTANCE; -@@ -743,6 +934,6 @@ - - private static class SingletonHolder - { -- protected static final GeoEngine INSTANCE = new GeoEngine(); -+ protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); - } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (working copy) -@@ -20,88 +20,84 @@ - import java.util.LinkedList; - import java.util.List; - import java.util.ListIterator; --import java.util.logging.Level; --import java.util.logging.Logger; - - import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; --import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; --import org.l2jmobius.gameserver.model.World; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -+import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.instancezone.Instance; - - /** -- * @author -Nemesiss- -+ * @author Hasha - */ --public class GeoEnginePathfinding -+final class GeoEnginePathfinding extends GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); -+ // pre-allocated buffers -+ private final BufferHolder[] _buffers; - -- private BufferInfo[] _buffers; -- - protected GeoEnginePathfinding() - { -- try -+ super(); -+ -+ final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ _buffers = new BufferHolder[array.length]; -+ int count = 0; -+ for (int i = 0; i < array.length; i++) - { -- final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ final String buf = array[i]; -+ final String[] args = buf.split("x"); - -- _buffers = new BufferInfo[array.length]; -- -- String buf; -- String[] args; -- for (int i = 0; i < array.length; i++) -+ try - { -- buf = array[i]; -- args = buf.split("x"); -- if (args.length != 2) -- { -- throw new Exception("Invalid buffer definition: " + buf); -- } -- -- _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); -+ final int size = Integer.parseInt(args[1]); -+ count += size; -+ _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); - } -+ catch (Exception e) -+ { -+ LOGGER.warning("GeoEnginePathfinding: Can not load buffer setting: " + buf); -+ } - } -- catch (Exception e) -- { -- LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); -- throw new Error("CellPathFinding: load aborted"); -- } -+ -+ LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); - } - -- public boolean pathNodesExist(short regionoffset) -+ @Override -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- return false; -- } -- -- public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) -- { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -- if (!GeoEngine.getInstance().hasGeo(x, y)) -+ // get origin and check existing geo coords -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { - 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)) -+ -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coords -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { - 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)))); -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // Prepare buffer for pathfinding calculations -+ final NodeBuffer buffer = getBuffer(64 + (2 * Math.max(Math.abs(gox - gtx), Math.abs(goy - gty)))); - if (buffer == null) - { - return null; - } - -- List path = null; -+ // find path -+ List path = null; - try - { -- final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); -- -+ final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); - if (result == null) - { - return null; -@@ -125,33 +121,45 @@ - return path; - } - -- int currentX, currentY, currentZ; -- ListIterator middlePoint; -+ // get path list iterator -+ final ListIterator point = path.listIterator(); - -- middlePoint = path.listIterator(); -- currentX = x; -- currentY = y; -- currentZ = z; -+ // get node A (origin) -+ int nodeAx = gox; -+ int nodeAy = goy; -+ short nodeAz = goz; - -- while (middlePoint.hasNext()) -+ // get node B -+ GeoLocation nodeB = (GeoLocation) point.next(); -+ -+ // iterate thought the path to optimize it -+ int count = 0; -+ while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) - { -- final AbstractNodeLoc locMiddle = middlePoint.next(); -- if (!middlePoint.hasNext()) -- { -- break; -- } -+ // get node C -+ final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - -- final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); -- if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) -+ // check movement from node A to node C -+ final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); -+ if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) - { -- middlePoint.remove(); -+ // can move from node A to node C -+ -+ // remove node B -+ point.remove(); - } - else - { -- currentX = locMiddle.getX(); -- currentY = locMiddle.getY(); -- currentZ = locMiddle.getZ(); -+ // can not move from node A to node C -+ -+ // set node A (node B is part of path, update A coordinates) -+ nodeAx = nodeB.getGeoX(); -+ nodeAy = nodeB.getGeoY(); -+ nodeAz = (short) nodeB.getZ(); - } -+ -+ // set node B -+ nodeB = (GeoLocation) point.next(); - } - - return path; -@@ -158,144 +166,102 @@ - } - - /** -- * Convert geodata position to pathnode position -- * @param geo_pos -- * @return pathnode position -+ * Create list of node locations as result of calculated buffer node tree. -+ * @param node : the entry point -+ * @return List : list of node location - */ -- public short getNodePos(int geo_pos) -+ private static List constructPath(Node node) - { -- return (short) (geo_pos >> 3); // OK? -- } -- -- /** -- * Convert node position to pathnode block position -- * @param node_pos -- * @return pathnode block position (0...255) -- */ -- public short getNodeBlock(int node_pos) -- { -- return (short) (node_pos % 256); -- } -- -- public byte getRegionX(int node_pos) -- { -- return (byte) ((node_pos >> 8) + World.TILE_X_MIN); -- } -- -- public byte getRegionY(int node_pos) -- { -- return (byte) ((node_pos >> 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 node_x rx -- * @return -- */ -- public int calculateWorldX(short node_x) -- { -- return World.MAP_MIN_X + (node_x * 128) + 48; -- } -- -- /** -- * Convert pathnode y to World y position -- * @param node_y -- * @return -- */ -- public int calculateWorldY(short node_y) -- { -- return World.MAP_MIN_Y + (node_y * 128) + 48; -- } -- -- private List constructPath(AbstractNode nodeValue) -- { -- final LinkedList path = new LinkedList<>(); -- int previousDirectionX = Integer.MIN_VALUE; -- int previousDirectionY = Integer.MIN_VALUE; -- int directionX, directionY; -+ // create empty list -+ final LinkedList list = new LinkedList<>(); - -- AbstractNode node = nodeValue; -- while (node.getParent() != null) -+ // set direction X/Y -+ int dx = 0; -+ int dy = 0; -+ -+ // get target parent -+ Node target = node; -+ Node parent = target.getParent(); -+ -+ // while parent exists -+ int count = 0; -+ while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { -- directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); -- directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); -+ // get parent <> target direction X/Y -+ final int nx = parent.getLoc().getGeoX() - target.getLoc().getGeoX(); -+ final int ny = parent.getLoc().getGeoY() - target.getLoc().getGeoY(); - -- // only add a new route point if moving direction changes -- if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) -+ // direction has changed? -+ if ((dx != nx) || (dy != ny)) - { -- previousDirectionX = directionX; -- previousDirectionY = directionY; -+ // add node to the beginning of the list -+ list.addFirst(target.getLoc()); - -- path.addFirst(node.getLoc()); -- node.setLoc(null); -+ // update direction X/Y -+ dx = nx; -+ dy = ny; - } - -- node = node.getParent(); -+ // move to next node, set target and get its parent -+ target = parent; -+ parent = target.getParent(); - } - -- return path; -+ // return list -+ return list; - } - -- private CellNodeBuffer alloc(int size) -+ /** -+ * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer. -+ * @param size : pre-calculated minimal required size -+ * @return NodeBuffer : buffer -+ */ -+ private final NodeBuffer getBuffer(int size) - { -- CellNodeBuffer current = null; -- for (BufferInfo i : _buffers) -+ NodeBuffer current = null; -+ for (BufferHolder holder : _buffers) - { -- if (i.mapSize >= size) -+ // Find proper size of buffer -+ if (holder._size < size) - { -- for (CellNodeBuffer buf : i.buffer) -+ continue; -+ } -+ -+ // Find unlocked NodeBuffer -+ for (NodeBuffer buffer : holder._buffer) -+ { -+ if (!buffer.isLocked()) - { -- if (buf.lock()) -- { -- current = buf; -- break; -- } -+ continue; - } -- if (current != null) -- { -- break; -- } - -- // not found, allocate temporary buffer -- current = new CellNodeBuffer(i.mapSize); -- current.lock(); -- if (i.buffer.size() < i.count) -- { -- i.buffer.add(current); -- break; -- } -+ return buffer; - } -+ -+ // NodeBuffer not found, allocate temporary buffer -+ current = new NodeBuffer(holder._size); -+ current.isLocked(); - } - - return current; - } - -- private static final class BufferInfo -+ /** -+ * NodeBuffer container with specified size and count of separate buffers. -+ */ -+ private static final class BufferHolder - { -- final int mapSize; -- final int count; -- ArrayList buffer; -+ final int _size; -+ List _buffer; - -- public BufferInfo(int size, int cnt) -+ public BufferHolder(int size, int count) - { -- mapSize = size; -- count = cnt; -- buffer = new ArrayList<>(count); -+ _size = size; -+ _buffer = new ArrayList<>(count); -+ for (int i = 0; i < count; i++) -+ { -+ _buffer.add(new NodeBuffer(size)); -+ } - } - } -- -- public static GeoEnginePathfinding getInstance() -- { -- return SingletonHolder.INSTANCE; -- } -- -- private static class SingletonHolder -- { -- protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); -- } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (working copy) -@@ -0,0 +1,197 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @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 height of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getHeightNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first above given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, above given coordinates. -+ */ -+ public abstract short getHeightAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first below given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is closest to given coordinates. -+ * @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 the NSWE flag byte of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getNsweNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first above given coordinates. -+ * @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 getNsweAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first below given coordinates. -+ * @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 getNsweBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is closes layer to given coordinates. -+ * @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. -+ * @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 above given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is first layer below given coordinates. -+ * @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 index to data of the cell, which is first layer below given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexBelowOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeight(int index); -+ -+ /** -+ * Returns the height of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightOriginal(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNswe(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNsweOriginal(int index); -+ -+ /** -+ * Sets the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @param nswe : New NSWE flag byte. -+ */ -+ public abstract void setNswe(int index, byte nswe); -+ -+ /** -+ * Saves the block in L2D format to {@link BufferedOutputStream}. Used only for L2D geodata conversion. -+ * @param stream : The stream. -+ * @throws IOException : Can't save the block to steam. -+ */ -+ public abstract void saveBlock(BufferedOutputStream stream) throws IOException; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (working copy) -@@ -0,0 +1,252 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockComplex(ByteBuffer bb, GeoFormat format) -+ { -+ // initialize buffer -+ _buffer = new byte[GeoStructure.BLOCK_CELLS * 3]; -+ -+ // load data -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // 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); -+ } -+ else -+ { -+ // get nswe -+ final byte nswe = bb.get(); -+ _buffer[i * 3] = nswe; -+ -+ // get height -+ final short height = bb.getShort(); -+ _buffer[(i * 3) + 1] = (byte) (height & 0x00FF); -+ _buffer[(i * 3) + 2] = (byte) (height >> 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height > worldZ ? height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height < worldZ ? height : Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public 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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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 int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_COMPLEX_L2D); -+ -+ // write block data -+ stream.write(_buffer, 0, GeoStructure.BLOCK_CELLS * 3); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (working copy) -@@ -0,0 +1,176 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockFlat extends ABlock -+{ -+ protected final short _height; -+ protected byte _nswe; -+ -+ /** -+ * Creates FlatBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockFlat(ByteBuffer bb, GeoFormat format) -+ { -+ _height = bb.getShort(); -+ _nswe = format != GeoFormat.L2D ? 0x0F : (byte) (0xFF); -+ if (format == GeoFormat.L2OFF) -+ { -+ bb.getShort(); -+ } -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return true; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height > worldZ ? _height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height < worldZ ? _height : Short.MAX_VALUE; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height > worldZ ? _nswe : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height < worldZ ? _nswe : 0; -+ } -+ -+ @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return index -+ return _height < worldZ ? 0 : -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _nswe = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_FLAT_L2D); -+ -+ // write height -+ stream.write((byte) (_height & 0x00FF)); -+ stream.write((byte) (_height >> 8)); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (working copy) -@@ -0,0 +1,465 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+import java.nio.ByteOrder; -+import java.util.Arrays; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockMultilayer extends ABlock -+{ -+ private static final int MAX_LAYERS = Byte.MAX_VALUE; -+ -+ private static ByteBuffer _temp; -+ -+ /** -+ * Initializes the temporarily buffer. -+ */ -+ public static void initialize() -+ { -+ // initialize temporarily buffer and sorting mechanism -+ _temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3); -+ _temp.order(ByteOrder.LITTLE_ENDIAN); -+ } -+ -+ /** -+ * Releases temporarily buffer. -+ */ -+ public static void release() -+ { -+ _temp = null; -+ } -+ -+ protected byte[] _buffer; -+ -+ /** -+ * Implicit constructor for children class. -+ */ -+ protected BlockMultilayer() -+ { -+ _buffer = null; -+ } -+ -+ /** -+ * Creates MultilayerBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockMultilayer(ByteBuffer bb, GeoFormat format) -+ { -+ // 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 = format != GeoFormat.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++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // get data -+ final short data = bb.getShort(); -+ -+ // add nswe and height -+ _temp.put((byte) (data & 0x000F)); -+ _temp.putShort((short) ((short) (data & 0xFFF0) >> 1)); -+ } -+ else -+ { -+ // add nswe -+ _temp.put(bb.get()); -+ -+ // add height -+ _temp.putShort(bb.getShort()); -+ } -+ } -+ } -+ -+ // 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 height -+ if (height > worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, return minimum value -+ return Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 height -+ if (height < worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, return maximum value -+ return Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 nswe -+ if (height > worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 nswe -+ if (height < worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @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 first layer data (first from bottom) -+ byte layers = _buffer[index++]; -+ -+ // loop though all cell layers, find closest layer -+ 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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ // get height -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(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]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ // get nswe -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ // set nswe -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_MULTILAYER_L2D); -+ -+ // for each cell -+ int index = 0; -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ // write layers count -+ final byte layers = _buffer[index++]; -+ stream.write(layers); -+ -+ // write cell data -+ stream.write(_buffer, index, layers * 3); -+ -+ // move index to next cell -+ index += layers * 3; -+ } -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (working copy) -@@ -0,0 +1,150 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockNull extends ABlock -+{ -+ private final byte _nswe; -+ -+ public BlockNull() -+ { -+ _nswe = (byte) 0xFF; -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return false; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweBelow(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) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) -+ { -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (nonexistent) -@@ -1,48 +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 . -- */ --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() -- { -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (nonexistent) -@@ -1,77 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (nonexistent) -@@ -1,56 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (working copy) -@@ -0,0 +1,39 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public enum GeoFormat -+{ -+ L2J("%d_%d.l2j"), -+ L2OFF("%d_%d_conv.dat"), -+ L2D("%d_%d.l2d"); -+ -+ private final String _filename; -+ -+ private GeoFormat(String filename) -+ { -+ _filename = filename; -+ } -+ -+ public String getFilename() -+ { -+ return _filename; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (working copy) -@@ -0,0 +1,67 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.geoengine.GeoEngine; -+import org.l2jmobius.gameserver.model.Location; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoLocation extends Location -+{ -+ private byte _nswe; -+ -+ public GeoLocation(int x, int y, int z) -+ { -+ super(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public void set(int x, int y, short z) -+ { -+ super.setXYZ(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public int getGeoX() -+ { -+ return _x; -+ } -+ -+ public int getGeoY() -+ { -+ return _y; -+ } -+ -+ @Override -+ public int getX() -+ { -+ return GeoEngine.getWorldX(_x); -+ } -+ -+ @Override -+ public int getY() -+ { -+ return GeoEngine.getWorldY(_y); -+ } -+ -+ public byte getNSWE() -+ { -+ return _nswe; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (working copy) -@@ -0,0 +1,70 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoStructure -+{ -+ // cells -+ public static final byte CELL_FLAG_E = 1 << 0; -+ public static final byte CELL_FLAG_W = 1 << 1; -+ public static final byte CELL_FLAG_S = 1 << 2; -+ public static final byte CELL_FLAG_N = 1 << 3; -+ public static final byte CELL_FLAG_SE = 1 << 4; -+ public static final byte CELL_FLAG_SW = 1 << 5; -+ public static final byte CELL_FLAG_NE = 1 << 6; -+ public static final byte CELL_FLAG_NW = (byte) (1 << 7); -+ -+ public static final int CELL_HEIGHT = 8; -+ public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6; -+ -+ // blocks -+ public static final byte TYPE_FLAT_L2J_L2OFF = 0; -+ public static final byte TYPE_FLAT_L2D = (byte) 0xD0; -+ public static final byte TYPE_COMPLEX_L2J = 1; -+ public static final byte TYPE_COMPLEX_L2OFF = 0x40; -+ public static final byte TYPE_COMPLEX_L2D = (byte) 0xD1; -+ 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) -+ public static final byte TYPE_MULTILAYER_L2D = (byte) 0xD2; -+ -+ 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; -+ -+ // regions -+ 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; -+ -+ // global geodata -+ private static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); -+ private 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; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (nonexistent) -@@ -1,42 +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 . -- */ --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); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (working copy) -@@ -0,0 +1,53 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public interface IGeoObject -+{ -+ /** -+ * Returns geodata X coordinate of the {@link IGeoObject}. -+ * @return int : Geodata X coordinate. -+ */ -+ int getGeoX(); -+ -+ /** -+ * Returns geodata Y coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Y coordinate. -+ */ -+ int getGeoY(); -+ -+ /** -+ * Returns geodata Z coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Z coordinate. -+ */ -+ int getGeoZ(); -+ -+ /** -+ * Returns height of the {@link IGeoObject}. -+ * @return int : Height. -+ */ -+ int getHeight(); -+ -+ /** -+ * Returns {@link IGeoObject} data. -+ * @return byte[][] : {@link IGeoObject} data. -+ */ -+ byte[][] getObjectGeoData(); -+} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (nonexistent) -@@ -1,47 +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 . -- */ --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(); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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); -- -- // 1 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (nonexistent) -@@ -1,55 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --/** -- * @author HorridoJoho -- */ --public final class NullRegion implements IRegion --{ -- public static final NullRegion INSTANCE = new NullRegion(); -- -- @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 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Region.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (nonexistent) -@@ -1,92 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (nonexistent) -@@ -1,87 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public abstract class AbstractNode --{ -- private T _loc; -- private AbstractNode _parent; -- -- public AbstractNode(T loc) -- { -- _loc = loc; -- } -- -- public void setParent(AbstractNode p) -- { -- _parent = p; -- } -- -- public AbstractNode getParent() -- { -- return _parent; -- } -- -- public T getLoc() -- { -- return _loc; -- } -- -- public void setLoc(T l) -- { -- _loc = l; -- } -- -- @Override -- public int hashCode() -- { -- final int prime = 31; -- int result = 1; -- result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); -- return result; -- } -- -- @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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (nonexistent) -@@ -1,33 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --/** -- * @author -Nemesiss- -- */ --public abstract class AbstractNodeLoc --{ -- public abstract int getX(); -- -- public abstract int getY(); -- -- public abstract int getZ(); -- -- public abstract int getNodeX(); -- -- public abstract int getNodeY(); --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (nonexistent) -@@ -1,67 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public class CellNode extends AbstractNode --{ -- 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (nonexistent) -@@ -1,348 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --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 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) -- { -- _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(); -- } -- -- public final List debugPath() -- { -- final List 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 void getNeighbors() -- { -- if (!_current.getLoc().canGoAll()) -- { -- 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); -- } -- -- // SouthEast -- if ((nodeE != null) && (nodeS != null)) -- { -- if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) -- { -- addNode(x + 1, y + 1, z, true); -- } -- } -- -- // SouthWest -- if ((nodeS != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) -- { -- addNode(x - 1, y + 1, z, true); -- } -- } -- -- // NorthEast -- if ((nodeN != null) && (nodeE != null)) -- { -- if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) -- { -- addNode(x + 1, y - 1, z, true); -- } -- } -- -- // NorthWest -- if ((nodeN != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) -- { -- addNode(x - 1, y - 1, z, true); -- } -- } -- } -- -- private 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(); -- // reinit node if needed -- if (result.getLoc() != null) -- { -- result.getLoc().set(x, y, z); -- } -- else -- { -- result.setLoc(new NodeLoc(x, y, z)); -- } -- } -- -- return result; -- } -- -- private 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 boolean isHighWeight(int x, int y, int z) -- { -- final CellNode result = getNode(x, y, z); -- if (result == null) -- { -- return true; -- } -- -- if (!result.getLoc().canGoAll()) -- { -- return true; -- } -- if (Math.abs(result.getLoc().getZ() - z) > 16) -- { -- return true; -- } -- -- return false; -- } -- -- private 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (working copy) -@@ -0,0 +1,87 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+ -+/** -+ * @author Hasha -+ */ -+public class Node -+{ -+ // node coords and nswe flag -+ private GeoLocation _loc; -+ -+ // node parent (for reverse path construction) -+ private Node _parent; -+ // node child (for moving over nodes during iteration) -+ private Node _child; -+ -+ // node G cost (movement cost = parent movement cost + current movement cost) -+ private double _cost = -1000; -+ -+ public void setLoc(int x, int y, int z) -+ { -+ _loc = new GeoLocation(x, y, z); -+ } -+ -+ public GeoLocation getLoc() -+ { -+ return _loc; -+ } -+ -+ public void setParent(Node parent) -+ { -+ _parent = parent; -+ } -+ -+ public Node getParent() -+ { -+ return _parent; -+ } -+ -+ public void setChild(Node child) -+ { -+ _child = child; -+ } -+ -+ public Node getChild() -+ { -+ return _child; -+ } -+ -+ public void setCost(double cost) -+ { -+ _cost = cost; -+ } -+ -+ public double getCost() -+ { -+ return _cost; -+ } -+ -+ public void free() -+ { -+ // reset node location -+ _loc = null; -+ -+ // reset node parent, child and cost -+ _parent = null; -+ _child = null; -+ _cost = -1000; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (working copy) -@@ -0,0 +1,306 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import java.util.concurrent.locks.ReentrantLock; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+ -+/** -+ * @author DS, Hasha; Credits to Diamond -+ */ -+public class NodeBuffer -+{ -+ private final ReentrantLock _lock = new ReentrantLock(); -+ private final int _size; -+ private final Node[][] _buffer; -+ -+ // center coordinates -+ private int _cx = 0; -+ private int _cy = 0; -+ -+ // target coordinates -+ private int _gtx = 0; -+ private int _gty = 0; -+ private short _gtz = 0; -+ -+ private Node _current = null; -+ -+ /** -+ * Constructor of NodeBuffer. -+ * @param size : one dimension size of buffer -+ */ -+ public NodeBuffer(int size) -+ { -+ // set size -+ _size = size; -+ -+ // initialize buffer -+ _buffer = new Node[size][size]; -+ for (int x = 0; x < size; x++) -+ { -+ for (int y = 0; y < size; y++) -+ { -+ _buffer[x][y] = 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 Node : first node of path -+ */ -+ public Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) -+ { -+ // set coordinates (middle of the line (gox,goy) - (gtx,gty), will be in the center of the buffer) -+ _cx = gox + ((gtx - gox - _size) / 2); -+ _cy = goy + ((gty - goy - _size) / 2); -+ -+ _gtx = gtx; -+ _gty = gty; -+ _gtz = gtz; -+ -+ _current = getNode(gox, goy, goz); -+ _current.setCost(getCostH(gox, goy, goz)); -+ -+ int count = 0; -+ do -+ { -+ // reached target? -+ if ((_current.getLoc().getGeoX() == _gtx) && (_current.getLoc().getGeoY() == _gty) && (Math.abs(_current.getLoc().getZ() - _gtz) < 8)) -+ { -+ return _current; -+ } -+ -+ // expand current node -+ expand(); -+ -+ // move pointer -+ _current = _current.getChild(); -+ } -+ while ((_current != null) && (count++ < Config.MAX_ITERATIONS)); -+ -+ return null; -+ } -+ -+ public boolean isLocked() -+ { -+ return _lock.tryLock(); -+ } -+ -+ public void free() -+ { -+ _current = null; -+ -+ for (Node[] nodes : _buffer) -+ { -+ for (Node node : nodes) -+ { -+ if (node.getLoc() != null) -+ { -+ node.free(); -+ } -+ } -+ } -+ -+ _lock.unlock(); -+ } -+ -+ /** -+ * Check _current Node and add its neighbors to the buffer. -+ */ -+ private final void expand() -+ { -+ // can't move anywhere, don't expand -+ final byte nswe = _current.getLoc().getNSWE(); -+ if (nswe == 0) -+ { -+ return; -+ } -+ -+ // get geo coords of the node to be expanded -+ final int x = _current.getLoc().getGeoX(); -+ final int y = _current.getLoc().getGeoY(); -+ final short z = (short) _current.getLoc().getZ(); -+ -+ // can move north, expand -+ if ((nswe & GeoStructure.CELL_FLAG_N) != 0) -+ { -+ addNode(x, y - 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move south, expand -+ if ((nswe & GeoStructure.CELL_FLAG_S) != 0) -+ { -+ addNode(x, y + 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_W) != 0) -+ { -+ addNode(x - 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_E) != 0) -+ { -+ addNode(x + 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move north-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NW) != 0) -+ { -+ addNode(x - 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move north-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NE) != 0) -+ { -+ addNode(x + 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SW) != 0) -+ { -+ addNode(x - 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SE) != 0) -+ { -+ addNode(x + 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ } -+ -+ /** -+ * Returns node, if it exists in buffer. -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param z : node Z coord -+ * @return Node : node, if exits in buffer -+ */ -+ private final Node getNode(int x, int y, short z) -+ { -+ // check node X out of coordinates -+ final int ix = x - _cx; -+ if ((ix < 0) || (ix >= _size)) -+ { -+ return null; -+ } -+ -+ // check node Y out of coordinates -+ final int iy = y - _cy; -+ if ((iy < 0) || (iy >= _size)) -+ { -+ return null; -+ } -+ -+ // get node -+ final Node result = _buffer[ix][iy]; -+ -+ // check and update -+ if (result.getLoc() == null) -+ { -+ result.setLoc(x, y, z); -+ } -+ -+ // return node -+ return result; -+ } -+ -+ /** -+ * Add node given by coordinates to the buffer. -+ * @param x : geo X coord -+ * @param y : geo Y coord -+ * @param z : geo Z coord -+ * @param weight : weight of movement to new node -+ */ -+ private final void addNode(int x, int y, short z, int weight) -+ { -+ // get node to be expanded -+ final Node node = getNode(x, y, z); -+ if (node == null) -+ { -+ return; -+ } -+ -+ // Z distance between nearby cells is higher than cell size -+ if (node.getLoc().getZ() > (z + (2 * GeoStructure.CELL_HEIGHT))) -+ { -+ return; -+ } -+ -+ // node was already expanded, return -+ if (node.getCost() >= 0) -+ { -+ return; -+ } -+ -+ node.setParent(_current); -+ if (node.getLoc().getNSWE() != (byte) 0xFF) -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + (weight * Config.OBSTACLE_MULTIPLIER)); -+ } -+ else -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + weight); -+ } -+ -+ Node current = _current; -+ int count = 0; -+ while ((current.getChild() != null) && (count < (Config.MAX_ITERATIONS * 4))) -+ { -+ count++; -+ if (current.getChild().getCost() > node.getCost()) -+ { -+ node.setChild(current.getChild()); -+ break; -+ } -+ current = current.getChild(); -+ } -+ -+ if (count >= (Config.MAX_ITERATIONS * 4)) -+ { -+ System.err.println("Pathfinding: too long loop detected, cost:" + node.getCost()); -+ } -+ -+ current.setChild(node); -+ } -+ -+ /** -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param i : node Z coord -+ * @return double : node cost -+ */ -+ private final double getCostH(int x, int y, int i) -+ { -+ final int dX = x - _gtx; -+ final int dY = y - _gty; -+ final int dZ = (i - _gtz) / GeoStructure.CELL_HEIGHT; -+ -+ // return (Math.abs(dX) + Math.abs(dY) + Math.abs(dZ)) * Config.HEURISTIC_WEIGHT; // Manhattan distance -+ return Math.sqrt((dX * dX) + (dY * dY) + (dZ * dZ)) * Config.HEURISTIC_WEIGHT; // Direct distance -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -- --/** -- * @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 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 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; -- // return super.hashCode(); -- } -- -- @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; -- if (_x != other._x) -- { -- return false; -- } -- if (_y != other._y) -- { -- return false; -- } -- if (_goNorth != other._goNorth) -- { -- return false; -- } -- if (_goEast != other._goEast) -- { -- return false; -- } -- if (_goSouth != other._goSouth) -- { -- return false; -- } -- if (_goWest != other._goWest) -- { -- return false; -- } -- if (_geoHeight != other._geoHeight) -- { -- return false; -- } -- return true; -- } --} -Index: java/org/l2jmobius/gameserver/model/actor/Creature.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/Creature.java (revision 8428) -+++ java/org/l2jmobius/gameserver/model/actor/Creature.java (working copy) -@@ -65,8 +65,6 @@ - import org.l2jmobius.gameserver.enums.TeleportWhereType; - import org.l2jmobius.gameserver.enums.UserInfoType; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; - import org.l2jmobius.gameserver.instancemanager.IdManager; - import org.l2jmobius.gameserver.instancemanager.MapRegionManager; - import org.l2jmobius.gameserver.instancemanager.QuestManager; -@@ -2566,7 +2564,7 @@ - - public boolean disregardingGeodata; - public int onGeodataPathIndex; -- public List geoPath; -+ public List geoPath; - public int geoPathAccurateTx; - public int geoPathAccurateTy; - public int geoPathGtx; -@@ -3443,7 +3441,7 @@ - if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) - { - // Path calculation -- overrides previous movement check -- m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); -+ m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); - if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found - { - if (isPlayer() && !_isFlying && !isInWater) -Index: java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (revision 8424) -+++ java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (working copy) -@@ -12746,7 +12746,7 @@ - { - if (Config.CORRECT_PLAYER_Z) - { -- final int nearestZ = GeoEngine.getInstance().getHigherHeight(getX(), getY(), getZ()); -+ final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); - if (getZ() < nearestZ) - { - teleToLocation(new Location(getX(), getY(), nearestZ)); -Index: java/org/l2jmobius/gameserver/util/GeoUtils.java -=================================================================== ---- java/org/l2jmobius/gameserver/util/GeoUtils.java (revision 8409) -+++ java/org/l2jmobius/gameserver/util/GeoUtils.java (working copy) -@@ -19,7 +19,7 @@ - import java.awt.Color; - - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; - import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; - -@@ -26,25 +26,25 @@ - /** - * @author HorridoJoho - */ --public final class GeoUtils -+public class GeoUtils - { - public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); - - final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); - - while (iter.next()) - { -- final int wx = GeoEngine.getInstance().getWorldX(iter.x()); -- final int wy = GeoEngine.getInstance().getWorldY(iter.y()); -+ final int wx = GeoEngine.getWorldX(iter.x()); -+ final int wy = GeoEngine.getWorldY(iter.y()); - - prim.addPoint(Color.RED, wx, wy, z); - } -@@ -53,21 +53,21 @@ - - public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); -- int wy = GeoEngine.getInstance().getWorldY(prevY); -+ int wx = GeoEngine.getWorldX(prevX); -+ int wy = GeoEngine.getWorldY(prevY); - int wz = iter.z(); - prim.addPoint(Color.RED, wx, wy, wz); - -@@ -78,8 +78,8 @@ - - if ((curX != prevX) || (curY != prevY)) - { -- wx = GeoEngine.getInstance().getWorldX(curX); -- wy = GeoEngine.getInstance().getWorldY(curY); -+ wx = GeoEngine.getWorldX(curX); -+ wy = GeoEngine.getWorldY(curY); - wz = iter.z(); - - prim.addPoint(Color.RED, wx, wy, wz); -@@ -93,7 +93,7 @@ - - private static Color getDirectionColor(int x, int y, int z, int nswe) - { -- if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) -+ if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) - { - return Color.GREEN; - } -@@ -109,9 +109,8 @@ - int iPacket = 0; - - ExServerPrimitive exsp = null; -- final GeoEngine ge = GeoEngine.getInstance(); -- final int playerGx = ge.getGeoX(player.getX()); -- final int playerGy = ge.getGeoY(player.getY()); -+ final int playerGx = GeoEngine.getGeoX(player.getX()); -+ final int playerGy = GeoEngine.getGeoY(player.getY()); - for (int dx = -geoRadius; dx <= geoRadius; ++dx) - { - for (int dy = -geoRadius; dy <= geoRadius; ++dy) -@@ -135,12 +134,12 @@ - final int gx = playerGx + dx; - final int gy = playerGy + dy; - -- final int x = ge.getWorldX(gx); -- final int y = ge.getWorldY(gy); -- final int z = ge.getNearestZ(gx, gy, player.getZ()); -+ final int x = GeoEngine.getWorldX(gx); -+ final int y = GeoEngine.getWorldY(gy); -+ final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); - - // north arrow -- Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); -+ Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); - 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); -@@ -147,7 +146,7 @@ - exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z); - - // east arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); - 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); -@@ -154,13 +153,13 @@ - exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z); - - // south arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); - 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, Cell.NSWE_WEST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); - 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); -@@ -188,15 +187,15 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_EAST; -+ return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_EAST; -+ return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; - } - else - { -- return Cell.NSWE_EAST; -+ return GeoStructure.CELL_FLAG_E; // Direction.EAST; - } - } - else if (x < lastX) // west -@@ -203,26 +202,27 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_WEST; -+ return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_WEST; -+ return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; - } - else - { -- return Cell.NSWE_WEST; -+ return GeoStructure.CELL_FLAG_W; // Direction.WEST; - } - } -- else // unchanged x -+ else -+ // unchanged x - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH; -+ return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH; -+ return GeoStructure.CELL_FLAG_N; // Direction.NORTH; - } - else - { -Index: java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java -=================================================================== ---- java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (nonexistent) -+++ java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (working copy) -@@ -0,0 +1,386 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.tools.geodataconverter; -+ -+import java.io.BufferedOutputStream; -+import java.io.File; -+import java.io.FileOutputStream; -+import java.io.RandomAccessFile; -+import java.nio.ByteOrder; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.Scanner; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.commons.util.PropertiesParser; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoDataConverter -+{ -+ private static GeoFormat _format; -+ private static ABlock[][] _blocks; -+ -+ public static void main(String[] args) -+ { -+ // initialize config -+ loadGeoengineConfigs(); -+ -+ // get geodata format -+ String type = ""; -+ try (Scanner scn = new Scanner(System.in)) -+ { -+ while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) -+ { -+ System.out.println("GeoDataConverter: Select source geodata type:"); -+ System.out.println(" J: L2J (e.g. 23_22.l2j)"); -+ System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); -+ System.out.println(" E: Exit"); -+ System.out.print("Choice: "); -+ type = scn.next(); -+ } -+ } -+ if (type.equalsIgnoreCase("E")) -+ { -+ System.exit(0); -+ } -+ -+ _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; -+ -+ // start conversion -+ System.out.println("GeoDataConverter: Converting all " + _format + " files."); -+ -+ // initialize geodata container -+ _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files -+ int converted = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) -+ { -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) -+ { -+ final String input = String.format(_format.getFilename(), rx, ry); -+ final String filepath = Config.GEODATA_PATH; -+ final File f = new File(filepath + input); -+ if (f.exists() && !f.isDirectory()) -+ { -+ // load geodata -+ if (!loadGeoBlocks(input)) -+ { -+ System.out.println("GeoDataConverter: Unable to load " + input + " region file."); -+ continue; -+ } -+ -+ // recalculate nswe -+ if (!recalculateNswe()) -+ { -+ System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); -+ continue; -+ } -+ -+ // save geodata -+ final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); -+ if (!saveGeoBlocks(output)) -+ { -+ System.out.println("GeoDataConverter: Unable to save " + output + " region file."); -+ continue; -+ } -+ -+ converted++; -+ System.out.println("GeoDataConverter: Created " + output + " region file."); -+ } -+ } -+ } -+ System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); -+ } -+ -+ /** -+ * Loads geo blocks from buffer of the region file. -+ * @param filename : The name of the to load. -+ * @return boolean : True when successful. -+ */ -+ private static boolean loadGeoBlocks(String filename) -+ { -+ // region file is load-able, try to load it -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) -+ { -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) -+ if (_format == GeoFormat.L2OFF) -+ { -+ for (int i = 0; i < 18; i++) -+ { -+ buffer.get(); -+ } -+ } -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ if (_format == GeoFormat.L2J) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2J: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2J: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ else -+ { -+ // get block type -+ final short type = buffer.getShort(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ default: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ if (buffer.remaining() > 0) -+ { -+ System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ return false; -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); -+ return false; -+ } -+ } -+ -+ /** -+ * Recalculate diagonal flags for the region file. -+ * @return boolean : True when successful. -+ */ -+ private static boolean recalculateNswe() -+ { -+ try -+ { -+ for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) -+ { -+ for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) -+ { -+ // get block -+ final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // skip flat blocks -+ if (block instanceof BlockFlat) -+ { -+ continue; -+ } -+ -+ // for complex and multilayer blocks go though all layers -+ short height = Short.MAX_VALUE; -+ int index; -+ while ((index = block.getIndexBelow(x, y, height)) != -1) -+ { -+ // get height and nswe -+ height = block.getHeight(index); -+ byte nswe = block.getNswe(index); -+ -+ // update nswe with diagonal flags -+ nswe = updateNsweBelow(x, y, height, nswe); -+ -+ // set nswe of the cell -+ block.setNswe(index, nswe); -+ } -+ } -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ /** -+ * Updates the NSWE flag with diagonal flags. -+ * @param x : Geodata X coordinate. -+ * @param y : Geodata Y coordinate. -+ * @param z : Geodata Z coordinate. -+ * @param nsweValue : NSWE flag to be updated. -+ * @return byte : Updated NSWE flag. -+ */ -+ private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) -+ { -+ byte nswe = nsweValue; -+ -+ // calculate virtual layer height -+ final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); -+ -+ // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) -+ final byte nsweN = getNsweBelow(x, y - 1, height); -+ final byte nsweS = getNsweBelow(x, y + 1, height); -+ final byte nsweW = getNsweBelow(x - 1, y, height); -+ final byte nsweE = getNsweBelow(x + 1, y, height); -+ -+ // north-west -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NW; -+ } -+ -+ // north-east -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NE; -+ } -+ -+ // south-west -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SW; -+ } -+ -+ // south-east -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SE; -+ } -+ -+ return nswe; -+ } -+ -+ private static byte getNsweBelow(int geoX, int geoY, short worldZ) -+ { -+ // out of geo coordinates -+ if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) -+ { -+ return 0; -+ } -+ -+ // out of geo coordinates -+ if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) -+ { -+ return 0; -+ } -+ -+ // get block -+ final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // get index, when valid, return nswe -+ final int index = block.getIndexBelow(geoX, geoY, worldZ); -+ return index == -1 ? 0 : block.getNswe(index); -+ } -+ -+ /** -+ * Save region file to file. -+ * @param filename : The name of file to save. -+ * @return boolean : True when successful. -+ */ -+ private static boolean saveGeoBlocks(String filename) -+ { -+ try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) -+ { -+ // loop over region blocks and save each block -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[ix][iy].saveBlock(bos); -+ } -+ } -+ -+ // flush data to file -+ bos.flush(); -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ private static void loadGeoengineConfigs() -+ { -+ final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); -+ Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); -+ Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); -+ Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); -+ Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); -+ Config.PATHFINDING = geoData.getBoolean("PathFinding", true); -+ Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -+ Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); -+ Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); -+ Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); -+ Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); -+ Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); -+ } -+} -\ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/geodata/Readme.txt b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/geodata/Readme.txt index a480c8c380..2611882e56 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/geodata/Readme.txt +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/geodata/Readme.txt @@ -23,8 +23,8 @@ a - Prerequisites b - Make it work ---------------------------------------------- -To make geodata working: -* unpack your geodata files into "/data/geodata" folder -* open "/config/GeoEngine.ini" with your favorite text editor and then edit following config: - - CoordSynchronize = 2 -* If you do not use any geodata files, the server will automatically change this setting to -1. +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. diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/ai/others/FleeMonsters.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/ai/others/FleeMonsters.java index 4562336103..a971742033 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/ai/others/FleeMonsters.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/ai/others/FleeMonsters.java @@ -59,7 +59,7 @@ public class FleeMonsters extends AbstractNpcAI final int posX = (int) (npc.getX() + (FLEE_DISTANCE * Math.cos(radians))); final int posY = (int) (npc.getY() + (FLEE_DISTANCE * Math.sin(radians))); final int posZ = npc.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, attacker.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); return super.onAttack(npc, attacker, damage, isSummon); } diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/ai/others/RandomWalkingGuards.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/ai/others/RandomWalkingGuards.java index b99becfdc6..780502d5d6 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/ai/others/RandomWalkingGuards.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/ai/others/RandomWalkingGuards.java @@ -55,7 +55,7 @@ public class RandomWalkingGuards extends AbstractNpcAI if (!npc.isInCombat()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, Config.MAX_DRIFT_RANGE); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("RANDOM_WALK", getRandom(MIN_WALK_DELAY, MAX_WALK_DELAY), npc, null); } diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index 524652cbc1..18511c6541 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { @@ -133,8 +133,8 @@ public class AdminGeodata implements IAdminCommandHandler } case "admin_geomap": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y + " (" + ((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + "," + ((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + " to " + ((((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + "," + ((((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + ")"); break; } diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java index f3dffa1227..76572f7f25 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java @@ -57,8 +57,8 @@ public class AdminMissingHtmls implements IAdminCommandHandler { case "admin_geomap_missing_htmls": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final int topLeftX = (x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE; final int topLeftY = (y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE; final int bottomRightX = (((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1; diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index fdb2319138..a0a34f1051 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -19,9 +19,9 @@ package handlers.admincommandhandlers; import java.util.List; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; +import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.util.BuilderUtil; @@ -44,13 +44,13 @@ public class AdminPathNode implements IAdminCommandHandler } if (activeChar.getTarget() != null) { - final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { BuilderUtil.sendSysMessage(activeChar, "No Route!"); return true; } - for (AbstractNodeLoc a : path) + for (Location a : path) { BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/Blink.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/Blink.java index 22f2c84eae..22697e8e20 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/Blink.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/Blink.java @@ -88,7 +88,7 @@ public class Blink extends AbstractEffect final int y = effected.getY() + y1; final int z = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, destination, _flyType, _flySpeed, _flyDelay, _animationSpeed)); diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/Fear.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/Fear.java index 95a5afdf98..88c795e04b 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/Fear.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/Fear.java @@ -100,7 +100,7 @@ public class Fear extends AbstractEffect final int posY = (int) (effected.getY() + (FEAR_RANGE * Math.sin(radians))); final int posZ = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); } } diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java index fddd94c60a..55c2e78d8a 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java @@ -57,7 +57,7 @@ public class FlyAway extends AbstractEffect final int y = (int) (effector.getY() - (nRadius * (dy / distance))); final int z = effector.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.broadcastPacket(new FlyToLocation(effected, destination, FlyType.THROW_UP)); effected.setXYZ(destination); diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java index 1fbd8239b0..89ad8670dc 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java @@ -133,7 +133,7 @@ public class KnockBack extends AbstractEffect final int x = (int) (effected.getX() + (_distance * Math.cos(radians))); final int y = (int) (effected.getY() + (_distance * Math.sin(radians))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, loc, _type, _speed, _delay, _animationSpeed)); diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/PullBack.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/PullBack.java index ed6a37eff6..38fe2fe9d0 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/PullBack.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/PullBack.java @@ -73,7 +73,7 @@ public class PullBack extends AbstractEffect } // In retail, you get debuff, but you are not even moved if there is obstacle. You are still disabled from using skills and moving though. - if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effector.getInstanceWorld())) + if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effected.getInstanceWorld())) { effected.broadcastPacket(new FlyToLocation(effected, effector, _type, _speed, _delay, _animationSpeed)); effected.setXYZ(effector.getX(), effector.getY(), GeoEngine.getInstance().getHeight(effector.getX(), effector.getY(), effector.getZ()) + 10); diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java index d6f9664141..813e496c47 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java @@ -87,7 +87,7 @@ public class TeleportToSummon extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = summon.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java index d3c263978c..c9dc34ac22 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java @@ -76,7 +76,7 @@ public class TeleportToTarget extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java index 99fbcb99ed..ffccf7fc7d 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java @@ -65,7 +65,7 @@ public class RollingDice implements IItemHandler final int x = player.getX() + x1; final int y = player.getY() + y1; final int z = player.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); Broadcast.toSelfAndKnownPlayers(player, new Dice(player.getObjectId(), itemId, number, destination.getX(), destination.getY(), destination.getZ())); final SystemMessage sm = new SystemMessage(SystemMessageId.C1_HAS_ROLLED_A_S2); diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/targethandlers/Ground.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/targethandlers/Ground.java index 82ab321c29..d8c73627f5 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/targethandlers/Ground.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/dist/game/data/scripts/handlers/targethandlers/Ground.java @@ -52,7 +52,7 @@ public class Ground implements ITargetTypeHandler return null; } - if (!GeoEngine.getInstance().canSeeTarget(creature, worldPosition)) + if (!GeoEngine.getInstance().canSeeLocation(creature, worldPosition)) { if (sendMessage) { diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/Config.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/Config.java index 465af5b620..c5385f2468 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/Config.java @@ -61,6 +61,7 @@ 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; @@ -928,12 +929,17 @@ public class Config // GeoEngine // -------------------------------------------------- public static Path GEODATA_PATH; + public static GeoType GEODATA_TYPE; public static boolean PATHFINDING; public static String PATHFIND_BUFFERS; - public static float LOW_WEIGHT; - public static float MEDIUM_WEIGHT; - public static float HIGH_WEIGHT; - public static float DIAGONAL_WEIGHT; + public static int MOVE_WEIGHT; + public static int MOVE_WEIGHT_DIAG; + public static int OBSTACLE_WEIGHT; + 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; /** Attribute System */ public static int S_WEAPON_STONE; @@ -2467,12 +2473,17 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); + GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); - MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); - HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); - DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); + MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); + OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); + HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); + MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); + MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); // Load AllowedPlayerRaces config file (if exists) final PropertiesParser AllowedPlayerRaces = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE); diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/commons/util/CommonUtil.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/commons/util/CommonUtil.java index 29a88cf20c..86fa98ff10 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/commons/util/CommonUtil.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/commons/util/CommonUtil.java @@ -591,6 +591,17 @@ public class CommonUtil return formatter.format(value); } + /** + * @param numToTest : The number to test. + * @param min : The minimum limit. + * @param max : The maximum limit. + * @return the number or one of the limit (mininum / maximum). + */ + public static int limit(int numToTest, int min, int max) + { + return (numToTest > max) ? max : ((numToTest < min) ? min : numToTest); + } + public static boolean isNullOrEmpty(CharSequence value) { return isNull(value) || (value.length() == 0); diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/commons/util/Point2D.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/commons/util/Point2D.java new file mode 100644 index 0000000000..ebb6272e5e --- /dev/null +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/commons/util/Point2D.java @@ -0,0 +1,180 @@ +/* + * 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 . + */ +package org.l2jmobius.commons.util; + +import java.util.Objects; + +/** + * A datatype used to retain a 2D (x/y) point. It got the capability to be set and cleaned. + */ +public class Point2D +{ + protected volatile int _x; + protected volatile int _y; + + public Point2D(int x, int y) + { + _x = x; + _y = y; + } + + @Override + public Point2D clone() + { + return new Point2D(_x, _y); + } + + @Override + public String toString() + { + return _x + ", " + _y; + } + + @Override + public int hashCode() + { + return Objects.hash(_x, _y); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final Point2D other = (Point2D) obj; + return (_x == other._x) && (_y == other._y); + } + + /** + * @param x : The X coord to test. + * @param y : The Y coord to test. + * @return True if all coordinates equals this {@link Point2D} coordinates. + */ + public boolean equals(int x, int y) + { + return (_x == x) && (_y == y); + } + + public int getX() + { + return _x; + } + + public void setX(int x) + { + _x = x; + } + + public int getY() + { + return _y; + } + + public void setY(int y) + { + _y = y; + } + + public void set(int x, int y) + { + _x = x; + _y = y; + } + + /** + * Refresh the current {@link Point2D} using a reference {@link Point2D} and a distance. The new destination is calculated to go in opposite side of the {@link Point2D} reference.
+ *
+ * This method is perfect to calculate fleeing characters position. + * @param referenceLoc : The Point2D used as position. + * @param distance : The distance to be set between current and new position. + */ + public void setFleeing(Point2D referenceLoc, int distance) + { + final double xDiff = referenceLoc.getX() - _x; + final double yDiff = referenceLoc.getY() - _y; + + final double yxRation = Math.abs(xDiff / yDiff); + + final int y = (int) (distance / (yxRation + 1)); + final int x = (int) (y * yxRation); + + _x += (xDiff < 0 ? x : -x); + _y += (yDiff < 0 ? y : -y); + } + + public void clean() + { + _x = 0; + _y = 0; + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @return The distance between this {@Point2D} and some given coordinates. + */ + public double distance2D(int x, int y) + { + final double dx = (double) _x - x; + final double dy = (double) _y - y; + + return Math.sqrt((dx * dx) + (dy * dy)); + } + + /** + * @param point : The {@link Point2D} to test. + * @return The distance between this {@Point2D} and the {@link Point2D} set as parameter. + */ + public double distance2D(Point2D point) + { + return distance2D(point.getX(), point.getY()); + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of some given coordinates. + */ + public boolean isIn2DRadius(int x, int y, int radius) + { + return distance2D(x, y) < radius; + } + + /** + * @param point : The Point2D to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of the {@link Point2D} set as parameter. + */ + public boolean isIn2DRadius(Point2D point, int radius) + { + return distance2D(point) < radius; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/ai/AttackableAI.java index 547963e3e8..dc29d4cf6e 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/ai/AttackableAI.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/ai/AttackableAI.java @@ -578,7 +578,7 @@ public class AttackableAI extends CreatureAI } // Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast) - final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); + final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); moveTo(moveLoc.getX(), moveLoc.getY(), moveLoc.getZ()); } } @@ -801,7 +801,7 @@ public class AttackableAI extends CreatureAI final int newZ = npc.getZ() + 30; // Mobius: Verify destination. Prevents wall collision issues and fixes monsters not avoiding obstacles. - moveTo(GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); + moveTo(GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); } return; } diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/data/SpawnTable.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/data/SpawnTable.java index a81201f69e..19f0db5b3e 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/data/SpawnTable.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/data/SpawnTable.java @@ -114,8 +114,8 @@ public class SpawnTable } // XML file for spawn - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); // Write info to XML @@ -192,8 +192,8 @@ public class SpawnTable if (update) { - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = spawn.getNpcSpawnTemplate() != null ? spawn.getNpcSpawnTemplate().getSpawnTemplate().getFile() : new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); final File tempFile = new File(spawnFile.getAbsolutePath().substring(Config.DATAPACK_ROOT.getAbsolutePath().length() + 1).replace('\\', '/') + ".tmp"); try diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/enums/GeoType.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/enums/GeoType.java new file mode 100644 index 0000000000..f77aa5eac4 --- /dev/null +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/enums/GeoType.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.enums; + +public enum GeoType +{ + L2J("%d_%d.l2j"), + L2OFF("%d_%d_conv.dat"); + + private final String _filename; + + private GeoType(String filename) + { + _filename = filename; + } + + public String getFilename() + { + return _filename; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java new file mode 100644 index 0000000000..e62f50a8df --- /dev/null +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java @@ -0,0 +1,144 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 9d255a5f54..7bef9c770b 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,72 +16,63 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.channels.FileChannel.MapMode; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.logging.Level; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; import java.util.logging.Logger; import org.l2jmobius.Config; +import org.l2jmobius.commons.util.CommonUtil; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; -import org.l2jmobius.gameserver.geoengine.geodata.IRegion; -import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; -import org.l2jmobius.gameserver.geoengine.geodata.Region; +import org.l2jmobius.gameserver.enums.GeoType; +import org.l2jmobius.gameserver.enums.MoveDirectionType; +import org.l2jmobius.gameserver.geoengine.geodata.ABlock; +import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; +import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; +import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; +import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.model.interfaces.ILocational; -import org.l2jmobius.gameserver.util.GeoUtils; -import org.l2jmobius.gameserver.util.LinePointIterator; -import org.l2jmobius.gameserver.util.LinePointIterator3D; -/** - * @author -Nemesiss-, HorridoJoho - */ public class GeoEngine { - private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - - private static final int WORLD_MIN_X = -655360; - private static final int WORLD_MIN_Y = -589824; - private static final int WORLD_MIN_Z = -16384; - - /** 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; - - /** The regions array */ - private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; - private static final int ELEVATED_SEE_OVER_DISTANCE = 2; - private static final int MAX_SEE_OVER_HEIGHT = 48; - protected GeoEngine() + private final ABlock[][] _blocks; + private final BlockNull _nullBlock; + + // Pre-allocated buffers. + private final BufferHolder[] _buffers; + + public GeoEngine() { LOGGER.info("GeoEngine: Initializing..."); - for (int i = 0; i < _regions.length(); i++) - { - _regions.set(i, NullRegion.INSTANCE); - } + // Initialize block container. + _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; + + // Load null block. + _nullBlock = new BlockNull(); + + // Initialize multilayer temporarily buffer. + BlockMultilayer.initialize(); + + // Load geo files according to geoengine config setup. int loaded = 0; try { @@ -92,23 +83,52 @@ public class GeoEngine final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); if (Files.exists(geoFilePath)) { - try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + // Region file is load-able, try to load it. + if (loadGeoBlocks(regionX, regionY)) { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); loaded++; } } + else + { + // Region file is not load-able, load null blocks. + loadNullBlocks(regionX, regionY); + } } } } catch (Exception e) { - LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + LOGGER.warning("GeoEngine: Failed to load geodata! " + e); System.exit(1); } - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + // Release multilayer block temporarily buffer. + BlockMultilayer.release(); + + String[] array = Config.PATHFIND_BUFFERS.split(";"); + _buffers = new BufferHolder[array.length]; + + int count = 0; + for (int i = 0; i < array.length; i++) + { + String buf = array[i]; + String[] args = buf.split("x"); + + try + { + int size = Integer.parseInt(args[1]); + count += size; + _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); + } + catch (Exception e) + { + LOGGER.warning("Could not load buffer setting:" + buf + ". " + e); + } + } + LOGGER.info("Loaded " + count + " node buffers."); + // Avoid wrong configs when no files are loaded. if ((loaded == 0) && Config.PATHFINDING) { @@ -118,616 +138,913 @@ public class GeoEngine } /** - * @param geoX - * @param geoY - * @return the region + * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer and log this situation. + * @param size : pre-calculated minimal required size + * @return NodeBuffer : buffer */ - private IRegion getRegion(int geoX, int geoY) + private NodeBuffer getBuffer(int size) { - final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); - if ((region < 0) || (region >= _regions.length())) + NodeBuffer current = null; + for (BufferHolder holder : _buffers) { - return null; - } - return _regions.get(region); - } - - /** - * @param filePath - * @param regionX - * @param regionY - * @throws IOException - */ - 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()).load().order(ByteOrder.LITTLE_ENDIAN))); - } - } - - /** - * @param regionX - * @param regionY - */ - public void unloadRegion(int regionX, int regionY) - { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); - } - - /** - * @param geoX - * @param geoY - * @return if geodata exist - */ - public boolean hasGeoPos(int geoX, int geoY) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return false; - } - return region.hasGeo(); - } - - /** - * Checks the specified position for available geodata. - * @param x the world x - * @param y the world y - * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise - */ - public boolean hasGeo(int x, int y) - { - return hasGeoPos(getGeoX(x), getGeoY(y)); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe check - */ - public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return true; - } - return region.checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe anti-corner cut - */ - public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) - { - boolean can = true; - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - return can && checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the nearest Z value - */ - public int getNearestZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNearestZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next lower Z value - */ - public int getNextLowerZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextLowerZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next higher Z value - */ - public int getNextHigherZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextHigherZ(geoX, geoY, worldZ); - } - - /** - * Gets the Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHeight(int x, int y, int z) - { - return getNearestZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next lower Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getLowerHeight(int x, int y, int z) - { - return getNextLowerZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next higher Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHigherHeight(int x, int y, int z) - { - return getNextHigherZ(getGeoX(x), getGeoY(y), z); - } - - /** - * @param worldX - * @return the geo X - */ - public int getGeoX(int worldX) - { - return (worldX - WORLD_MIN_X) / 16; - } - - /** - * @param worldY - * @return the geo Y - */ - public int getGeoY(int worldY) - { - return (worldY - WORLD_MIN_Y) / 16; - } - - /** - * @param worldZ - * @return the geo Z - */ - public int getGeoZ(int worldZ) - { - return (worldZ - WORLD_MIN_Z) / 16; - } - - /** - * @param geoX - * @return the world X - */ - public int getWorldX(int geoX) - { - return (geoX * 16) + WORLD_MIN_X + 8; - } - - /** - * @param geoY - * @return the world Y - */ - public int getWorldY(int geoY) - { - return (geoY * 16) + WORLD_MIN_Y + 8; - } - - /** - * @param geoZ - * @return the world Z - */ - public int getWorldZ(int geoZ) - { - return (geoZ * 16) + WORLD_MIN_Z + 8; - } - - /** - * Can see target. Doors as target always return true. Checks doors between. - * @param cha the character - * @param target the target - * @return {@code true} if the character can see the target (LOS), {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, WorldObject target) - { - if (target.isDoor()) - { - // Can always see doors. - return true; - } - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); - } - - /** - * Can see target. Checks doors between. - * @param cha the character - * @param worldPosition the world position - * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) - { - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param world - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @param tworld the target's instanceId - * @return - */ - public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) - { - return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instance - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) - { - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) - { - return false; - } - return canSeeTarget(x, y, z, tx, ty, tz); - } - - /** - * @param prevX - * @param prevY - * @param prevGeoZ - * @param curX - * @param curY - * @param nswe - * @return the LOS Z value - */ - private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) - { - if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) - { - throw new RuntimeException("Multiple directions!"); - } - - if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) - { - return getNearestZ(curX, curY, prevGeoZ); - } - - return getNextHigherZ(curX, curY, prevGeoZ); - } - - /** - * Can see target. Does not check doors between. - * @param xValue the x coordinate - * @param yValue the y coordinate - * @param zValue the z coordinate - * @param txValue the target's x coordinate - * @param tyValue the target's y coordinate - * @param tzValue the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) - { - int x = xValue; - int y = yValue; - int tx = txValue; - int ty = tyValue; - - int geoX = getGeoX(x); - int geoY = getGeoY(y); - int tGeoX = getGeoX(tx); - int tGeoY = getGeoY(ty); - - int z = getNearestZ(geoX, geoY, zValue); - int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - // Fastpath. - if ((geoX == tGeoX) && (geoY == tGeoY)) - { - if (hasGeoPos(tGeoX, tGeoY)) - { - return z == tz; - } - return true; - } - - if (tz > z) - { - int tmp = tx; - tx = x; - x = tmp; - - tmp = ty; - ty = y; - y = tmp; - - tmp = tz; - tz = z; - z = tmp; - - tmp = tGeoX; - tGeoX = geoX; - geoX = tmp; - - tmp = tGeoY; - tGeoY = geoY; - geoY = tmp; - } - - final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); - // First point is guaranteed to be available, skip it, we can always see our own position. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - final int prevZ = pointIter.z(); - int prevGeoZ = prevZ; - int ptIndex = 0; - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - - if ((curX == prevX) && (curY == prevY)) + // Find proper size of buffer. + if (holder._size < size) { continue; } - final int beeCurZ = pointIter.z(); - int curGeoZ = prevGeoZ; - - // Check if the position has geodata. - if (hasGeoPos(curX, curY)) + // Find unlocked NodeBuffer. + for (NodeBuffer buffer : holder._buffer) { - final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); - curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); - int maxHeight; - if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) + if (!buffer.isLocked()) { - maxHeight = z + MAX_SEE_OVER_HEIGHT; - } - else - { - maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; + continue; } - boolean canSeeThrough = false; - if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) + return buffer; + } + + LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); + + // NodeBuffer not found, allocate temporary buffer. + current = new NodeBuffer(holder._size); + current.isLocked(); + } + + return current; + } + + /** + * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + * @return boolean : True, when geodata file was loaded without problem. + */ + private boolean loadGeoBlocks(int regionX, int regionY) + { + final String filename = String.format(Config.GEODATA_TYPE.getFilename(), regionX, regionY); + final String filepath = Config.GEODATA_PATH + "\\" + filename; + + // Standard load. + try (RandomAccessFile raf = new RandomAccessFile(filepath, "r"); + FileChannel fc = raf.getChannel()) + { + // Initialize file buffer. + MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // Load 18B header for L2OFF geodata (1st and 2nd byte...region X and Y). + if (Config.GEODATA_TYPE == GeoType.L2OFF) + { + for (int i = 0; i < 18; i++) { - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) + buffer.get(); + } + } + + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Loop over region blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + if (Config.GEODATA_TYPE == GeoType.L2J) { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) - { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); + // Get block type. + final byte type = buffer.get(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + case GeoStructure.TYPE_MULTILAYER_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + default: + { + throw new IllegalArgumentException("Unknown block type: " + type); + } + } } else { - canSeeThrough = true; + // Get block type. + final short type = buffer.getShort(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + default: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + } } } + } + + // Check data consistency. + if (buffer.remaining() > 0) + { + LOGGER.warning("Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); + } + + // Loading was successful. + return true; + } + catch (Exception e) + { + // An error occured while loading, load null blocks. + LOGGER.warning("Error loading " + filename + " region file. " + e); + + // Replace whole region file with null blocks. + loadNullBlocks(regionX, regionY); + + // Loading was not successful. + return false; + } + } + + /** + * Loads null blocks. Used when no region file is detected or an error occurs during loading. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + */ + private void loadNullBlocks(int regionX, int regionY) + { + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Load all null blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + _blocks[blockX + ix][blockY + iy] = _nullBlock; + } + } + } + + /** + * Converts world X to geodata X. + * @param worldX + * @return int : Geo X + */ + public static int getGeoX(int worldX) + { + return (worldX - World.WORLD_X_MIN) >> 4; + } + + /** + * Converts world Y to geodata Y. + * @param worldY + * @return int : Geo Y + */ + public static int getGeoY(int worldY) + { + return (worldY - World.WORLD_Y_MIN) >> 4; + } + + /** + * Converts geodata X to world X. + * @param geoX + * @return int : World X + */ + public static int getWorldX(int geoX) + { + return (geoX << 4) + World.WORLD_X_MIN + 8; + } + + /** + * Converts geodata Y to world Y. + * @param geoY + * @return int : World Y + */ + public static int getWorldY(int geoY) + { + return (geoY << 4) + World.WORLD_Y_MIN + 8; + } + + /** + * Returns block of geodata on given coordinates. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return {@link ABlock} : Block of geodata. + */ + public ABlock getBlock(int geoX, int geoY) + { + final int x = geoX / GeoStructure.BLOCK_CELLS_X; + if ((x < 0) || (x >= GeoStructure.GEO_BLOCKS_X)) + { + return null; + } + final int y = geoY / GeoStructure.BLOCK_CELLS_Y; + if ((y < 0) || (y >= GeoStructure.GEO_BLOCKS_Y)) + { + return null; + } + return _blocks[x][y]; + } + + /** + * Check if geo coordinates has geo. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return boolean : True, if given geo coordinates have geodata + */ + public boolean hasGeoPos(int geoX, int geoY) + { + final ABlock block = getBlock(geoX, geoY); + return (block != null) && block.hasGeoPos(); + } + + /** + * Returns the height of cell, which is closest to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell geodata Z coordinate, closest to given coordinates. + */ + public short getHeightNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return (short) worldZ; + } + return block.getHeightNearest(geoX, geoY, worldZ); + } + + /** + * Returns the NSWE flag byte of cell, which is closes to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. + */ + public byte getNsweNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return GeoStructure.CELL_FLAG_ALL; + } + return block.getNsweNearest(geoX, geoY, worldZ); + } + + /** + * Check if world coordinates has geo. + * @param worldX : World X + * @param worldY : World Y + * @return boolean : True, if given world coordinates have geodata + */ + public boolean hasGeo(int worldX, int worldY) + { + return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param loc : The location used as reference. + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(Location loc) + { + return getHeightNearest(getGeoX(loc.getX()), getGeoY(loc.getY()), loc.getZ()); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param worldX : world x + * @param worldY : world y + * @param worldZ : world z + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(int worldX, int worldY, int worldZ) + { + return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); + } + + /** + * Check line of sight from {@link WorldObject} to {@link WorldObject}.
+ * @param object : The origin object. + * @param target : The target object. + * @return True, when object can see target. + */ + public boolean canSeeTarget(WorldObject object, WorldObject target) + { + // Can always see doors. + if (target.isDoor()) + { + return true; + } + + if (object.getInstanceWorld() != target.getInstanceWorld()) + { + return false; + } + + if (DoorData.getInstance().checkIfDoorsBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld())) + { + return false; + } + + // Get object's and target's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + double theight = 0; + if (target instanceof Creature) + { + theight += (((Creature) target).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + return canSee(object.getX(), object.getY(), object.getZ(), oheight, target.getX(), target.getY(), target.getZ(), theight) && canSee(target.getX(), target.getY(), target.getZ(), theight, object.getX(), object.getY(), object.getZ(), oheight); + } + + /** + * Check line of sight from {@link WorldObject} to {@link Location}.
+ * Note: The check uses {@link Location}'s real Z coordinate (e.g. point above ground), not its geodata representation. + * @param object : The origin object. + * @param position : The target position. + * @return True, when object can see position. + */ + public boolean canSeeLocation(WorldObject object, Location position) + { + // Get object and location coordinates. + int ox = object.getX(); + int oy = object.getY(); + int oz = object.getZ(); + int tx = position.getX(); + int ty = position.getY(); + int tz = position.getZ(); + + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld())) + { + return false; + } + + // Get object's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + // Perform geodata check. + return canSee(ox, oy, oz, oheight, tx, ty, tz, 0) && canSee(tx, ty, tz, 0, ox, oy, oz, oheight); + } + + /** + * Simple check for origin to target visibility.
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param oheight : The height of origin, used as start point. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param theight : The height of target, used as end point. + * @return True, when origin can see target. + */ + public boolean canSee(int ox, int oy, int oz, double oheight, int tx, int ty, int tz, double theight) + { + // Get origin geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get target geodata coordinates. + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check being on same cell and layer (index). + // Note: Get index must use origin height increased by cell height, the method returns index to height exclusive self. + int index = block.getIndexBelow(gox, goy, oz + GeoStructure.CELL_HEIGHT); + if (index < 0) + { + return false; + } + + if ((gox == gtx) && (goy == gty)) + { + return index == block.getIndexBelow(gtx, gty, tz + GeoStructure.CELL_HEIGHT); + } + + // Get ground and nswe flag. + int groundZ = block.getHeight(index); + int nswe = block.getNswe(index); + + // Get delta coordinates, slope of line (XY, XZ) and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double dz = (tz + theight) - (oz + oheight); + final double m = (double) dy / dx; + final double mz = dz / Math.sqrt((dx * dx) + (dy * dy)); + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); + + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) + { + // Set next cell in X direction. + gridX += mdt.getStepX(); + gox += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); - if (!canSeeThrough) - { - return false; - } + // Set next cell in Y direction. + gridY += mdt.getStepY(); + goy += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevGeoZ = curGeoZ; - ++ptIndex; + // Get block of the next cell. + block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get line of sight height (including Z slope). + double losz = oz + oheight + Config.MAX_OBSTACLE_HEIGHT; + losz += mz * Math.sqrt(((checkX - ox) * (checkX - ox)) + ((checkY - oy) * (checkY - oy))); + + // Check line of sight going though wall (vertical check). + + // Get index of particular layer, based on last iterated cell conditions. + boolean canMove = (nswe & dir) != 0; + if (canMove) + { + // No wall present, get next cell below current cell. + index = block.getIndexBelow(gox, goy, groundZ + GeoStructure.CELL_IGNORE_HEIGHT); + } + else + { + // Wall present, get next cell above current cell. + index = block.getIndexAbove(gox, goy, groundZ - (2 * GeoStructure.CELL_HEIGHT)); + } + + // Next cell's does not exist (no geodata with valid condition), return fail. + if (index < 0) + { + return false; + } + + // Get next cell's layer height. + int z = block.getHeight(index); + + // Perform sine of sight check (next cell is above line of sight line), return fail. + if (z > losz) + { + return false; + } + + // Next cell is accessible, update z and NSWE. + groundZ = z; + nswe = block.getNswe(index); } + // Iteration is completed, no obstacle is found. return true; } /** - * Move check. - * @param x the x coordinate - * @param y the y coordinate - * @param zValue the z coordinate - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tzValue the target's z coordinate - * @param instance the instance - * @return the last Location (x,y,z) where player can walk - just before wall + * Check movement from coordinates to coordinates.
+ * Note: The Z coordinates are supposed to be already validated geodata coordinates. + * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return True, when target coordinates are reachable from origin coordinates. */ - public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(x); - final int geoY = getGeoY(y); - final int z = getNearestZ(geoX, geoY, zValue); - final int tGeoX = getGeoX(tx); - final int tGeoY = getGeoY(ty); - final int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return new Location(x, y, getHeight(x, y, z)); - } - if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) - { - return new Location(x, y, getHeight(x, y, z)); + return false; } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = z; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return false; + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + int goz = getHeightNearest(gox, goy, oz); + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check movement within same cell. + if ((gox == gtx) && (goy == gty)) + { + return goz == getHeight(tx, ty, tz); + } + + // Get nswe flag. + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - // Can't move, return previous location. - return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check point heading into obstacle, if so return current point. + if ((nswe & dir) == 0) + { + return false; + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return false; + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != tz)) - { - // Different floors, return start location. - return new Location(x, y, z); - } - - return new Location(tx, ty, tz); + // When origin Z is target Z, the move is successful. + return goz == getHeight(tx, ty, tz); } /** - * Checks if its possible to move from one location to another. - * @param fromX the X coordinate to start checking from - * @param fromY the Y coordinate to start checking from - * @param fromZvalue the Z coordinate to start checking from - * @param toX the X coordinate to end checking at - * @param toY the Y coordinate to end checking at - * @param toZvalue the Z coordinate to end checking at - * @param instance the instance - * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + * Check movement from origin to target coordinates. Returns last available point in the checked path.
+ * Target X and Y reachable and Z is on same floor: + *
    + *
  • Location of the target with corrected Z value from geodata.
  • + *
+ * Target X and Y reachable but Z is on another floor: + *
    + *
  • Location of the origin with corrected Z value from geodata.
  • + *
+ * Target X and Y not reachable: + *
    + *
  • Last accessible location in destination to target.
  • + *
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return The {@link Location} representing last point of movement (e.g. just before wall). */ - public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + public Location getValidLocation(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(fromX); - final int geoY = getGeoY(fromY); - final int fromZ = getNearestZ(geoX, geoY, fromZvalue); - final int tGeoX = getGeoX(toX); - final int tGeoY = getGeoY(toY); - final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); - - if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) - { - return false; + return new Location(ox, oy, oz); } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = fromZ; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return new Location(ox, oy, oz); + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + final int gtz = getHeightNearest(gtx, gty, tz); + int goz = getHeightNearest(gox, goy, oz); + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - return false; - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check target cell is out of geodata grid (world coordinates). + if ((nx < 0) || (nx >= GeoStructure.GEO_CELLS_X) || (ny < 0) || (ny >= GeoStructure.GEO_CELLS_Y)) + { + return new Location(checkX, checkY, goz); + } + + // Check point heading into obstacle, if so return current (border) point. + if ((nswe & dir) == 0) + { + return new Location(checkX, checkY, goz); + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current (border) point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return new Location(checkX, checkY, goz); + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) - { - // Different floors. - return false; - } - - return true; + // Compare Z coordinates: + // If same, path is okay, return target point and fix its Z geodata coordinate. + // If not same, path is does not exist, return origin point. + return goz == gtz ? new Location(tx, ty, gtz) : new Location(ox, oy, oz); } + /** + * Returns the list of location objects as a result of complete path calculation. + * @param ox : origin x + * @param oy : origin y + * @param oz : origin z + * @param tx : target x + * @param ty : target y + * @param tz : target z + * @param instance + * @return {@code List} : complete path from nodes + */ + public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + { + // Get origin and check existing geo coords. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + if (!hasGeoPos(gox, goy)) + { + return Collections.emptyList(); + } + + int goz = getHeightNearest(gox, goy, oz); + + // Get target and check existing geo coords. + int gtx = getGeoX(tx); + int gty = getGeoY(ty); + if (!hasGeoPos(gtx, gty)) + { + return Collections.emptyList(); + } + + int gtz = getHeightNearest(gtx, gty, tz); + + // Prepare buffer for pathfinding calculations. + NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + if (buffer == null) + { + return Collections.emptyList(); + } + + // Find path. + List path = null; + try + { + path = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + if (path.isEmpty()) + { + return Collections.emptyList(); + } + } + catch (Exception e) + { + return Collections.emptyList(); + } + finally + { + buffer.free(); + } + + // Check path. + if (path.size() < 3) + { + return path; + } + + // Get path list iterator. + ListIterator point = path.listIterator(); + + // Get node A (origin). + int nodeAx = ox; + int nodeAy = oy; + int nodeAz = goz; + + // Get node B. + Location nodeB = point.next(); + + // Iterate thought the path to optimize it. + while (point.hasNext()) + { + // Get node C. + Location nodeC = path.get(point.nextIndex()); + + // Check movement from node A to node C. + if (canMoveToTarget(nodeAx, nodeAy, nodeAz, nodeC.getX(), nodeC.getY(), nodeC.getZ(), instance)) + { + // Can move from node A to node C. + // Remove node B. + point.remove(); + } + else + { + // Can not move from node A to node C. + // Set node A (node B is part of path, update A coordinates). + nodeAx = nodeB.getX(); + nodeAy = nodeB.getY(); + nodeAz = nodeB.getZ(); + } + + // Set node B. + nodeB = point.next(); + } + + return path; + } + + /** + * NodeBuffer container with specified size and count of separate buffers. + */ + private static class BufferHolder + { + final int _size; + final List _buffer; + + public BufferHolder(int size, int count) + { + _size = size; + _buffer = new ArrayList<>(count); + + for (int i = 0; i < count; i++) + { + _buffer.add(new NodeBuffer(size)); + } + } + } + + /** + * Returns the instance of the {@link GeoEngine}. + * @return {@link GeoEngine} : The instance. + */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -737,4 +1054,4 @@ public class GeoEngine { protected static final GeoEngine INSTANCE = new GeoEngine(); } -} +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java deleted file mode 100644 index cc76b7523a..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ /dev/null @@ -1,301 +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 . - */ -package org.l2jmobius.gameserver.geoengine; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; -import org.l2jmobius.gameserver.model.World; -import org.l2jmobius.gameserver.model.instancezone.Instance; - -/** - * @author -Nemesiss- - */ -public class GeoEnginePathfinding -{ - private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); - - private BufferInfo[] _buffers; - - protected GeoEnginePathfinding() - { - try - { - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - - _buffers = 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); - } - - _buffers[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"); - } - } - - public boolean pathNodesExist(short regionoffset) - { - return false; - } - - public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) - { - 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)))); - if (buffer == null) - { - return null; - } - - List path = null; - try - { - final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); - - if (result == null) - { - return null; - } - - path = constructPath(result); - } - catch (Exception e) - { - LOGGER.warning(e.getMessage()); - return null; - } - finally - { - buffer.free(); - } - - // check path - if (path.size() < 3) - { - return path; - } - - int currentX, currentY, currentZ; - ListIterator middlePoint; - - middlePoint = path.listIterator(); - currentX = x; - currentY = y; - currentZ = z; - - while (middlePoint.hasNext()) - { - final AbstractNodeLoc locMiddle = middlePoint.next(); - if (!middlePoint.hasNext()) - { - break; - } - - final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); - if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) - { - middlePoint.remove(); - } - else - { - currentX = locMiddle.getX(); - currentY = locMiddle.getY(); - currentZ = locMiddle.getZ(); - } - } - - return path; - } - - /** - * Convert geodata position to pathnode position - * @param geo_pos - * @return pathnode position - */ - public short getNodePos(int geo_pos) - { - return (short) (geo_pos >> 3); // OK? - } - - /** - * Convert node position to pathnode block position - * @param node_pos - * @return pathnode block position (0...255) - */ - public short getNodeBlock(int node_pos) - { - return (short) (node_pos % 256); - } - - public byte getRegionX(int node_pos) - { - return (byte) ((node_pos >> 8) + World.TILE_X_MIN); - } - - public byte getRegionY(int node_pos) - { - return (byte) ((node_pos >> 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 node_x rx - * @return - */ - public int calculateWorldX(short node_x) - { - return World.MAP_MIN_X + (node_x * 128) + 48; - } - - /** - * Convert pathnode y to World y position - * @param node_y - * @return - */ - public int calculateWorldY(short node_y) - { - return World.MAP_MIN_Y + (node_y * 128) + 48; - } - - private List constructPath(AbstractNode nodeValue) - { - final LinkedList path = new LinkedList<>(); - int previousDirectionX = Integer.MIN_VALUE; - int previousDirectionY = Integer.MIN_VALUE; - int directionX, directionY; - - AbstractNode node = nodeValue; - while (node.getParent() != null) - { - directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); - directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); - - // only add a new route point if moving direction changes - if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) - { - previousDirectionX = directionX; - previousDirectionY = directionY; - - path.addFirst(node.getLoc()); - node.setLoc(null); - } - - node = node.getParent(); - } - - return path; - } - - private CellNodeBuffer alloc(int size) - { - CellNodeBuffer current = null; - for (BufferInfo i : _buffers) - { - if (i.mapSize >= size) - { - for (CellNodeBuffer buf : i.buffer) - { - if (buf.lock()) - { - current = buf; - break; - } - } - if (current != null) - { - break; - } - - // not found, allocate temporary buffer - current = new CellNodeBuffer(i.mapSize); - current.lock(); - if (i.buffer.size() < i.count) - { - i.buffer.add(current); - break; - } - } - } - - return current; - } - - private static final class BufferInfo - { - final int mapSize; - final int count; - ArrayList buffer; - - public BufferInfo(int size, int cnt) - { - mapSize = size; - count = cnt; - buffer = new ArrayList<>(count); - } - } - - public static GeoEnginePathfinding getInstance() - { - return SingletonHolder.INSTANCE; - } - - private static class SingletonHolder - { - protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); - } -} diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java new file mode 100644 index 0000000000..49ec35aa1c --- /dev/null +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java @@ -0,0 +1,85 @@ +/* + * 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 . + */ +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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @param index : Index of the cell. + * @return short : Cell geodata Z coordinate, below given coordinates. + */ + public abstract byte getNswe(int index); +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java new file mode 100644 index 0000000000..f5997bf287 --- /dev/null +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java @@ -0,0 +1,130 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java new file mode 100644 index 0000000000..9af9d2a28a --- /dev/null +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java @@ -0,0 +1,95 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java new file mode 100644 index 0000000000..31fbf5d477 --- /dev/null +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java new file mode 100644 index 0000000000..f77d21d84b --- /dev/null +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java @@ -0,0 +1,68 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +public class BlockNull extends ABlock +{ + @Override + public boolean hasGeoPos() + { + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java deleted file mode 100644 index 61ea4fcf82..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java +++ /dev/null @@ -1,48 +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 . - */ -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() - { - } -} diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java deleted file mode 100644 index 3c8de1a7f7..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java +++ /dev/null @@ -1,77 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java deleted file mode 100644 index 1ccdefe3da..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java +++ /dev/null @@ -1,56 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java new file mode 100644 index 0000000000..691b164e0c --- /dev/null +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ +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_ALL = 0x0F; + + // 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; +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java deleted file mode 100644 index 25eafc5a04..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java +++ /dev/null @@ -1,42 +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 . - */ -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); -} diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java deleted file mode 100644 index 0d2c765002..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java +++ /dev/null @@ -1,47 +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 . - */ -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(); -} diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java deleted file mode 100644 index 52df457360..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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); - - // 1 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; - } -} diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java deleted file mode 100644 index 72a5a635c7..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java +++ /dev/null @@ -1,55 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author HorridoJoho - */ -public final class NullRegion implements IRegion -{ - public static final NullRegion INSTANCE = new NullRegion(); - - @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 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; - } -} diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java deleted file mode 100644 index fc924cb875..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java +++ /dev/null @@ -1,92 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java deleted file mode 100644 index 5087513ed9..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java +++ /dev/null @@ -1,87 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public abstract class AbstractNode -{ - private T _loc; - private AbstractNode _parent; - - public AbstractNode(T loc) - { - _loc = loc; - } - - public void setParent(AbstractNode p) - { - _parent = p; - } - - public AbstractNode getParent() - { - return _parent; - } - - public T getLoc() - { - return _loc; - } - - public void setLoc(T l) - { - _loc = l; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); - return result; - } - - @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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java deleted file mode 100644 index 7223b5b2be..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java +++ /dev/null @@ -1,33 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -/** - * @author -Nemesiss- - */ -public abstract class AbstractNodeLoc -{ - public abstract int getX(); - - public abstract int getY(); - - public abstract int getZ(); - - public abstract int getNodeX(); - - public abstract int getNodeY(); -} diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java deleted file mode 100644 index 3425234e0e..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public class CellNode extends AbstractNode -{ - 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java deleted file mode 100644 index 522610bb7c..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java +++ /dev/null @@ -1,348 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -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 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) - { - _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(); - } - - public final List debugPath() - { - final List 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 void getNeighbors() - { - if (!_current.getLoc().canGoAll()) - { - 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); - } - - // SouthEast - if ((nodeE != null) && (nodeS != null)) - { - if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) - { - addNode(x + 1, y + 1, z, true); - } - } - - // SouthWest - if ((nodeS != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) - { - addNode(x - 1, y + 1, z, true); - } - } - - // NorthEast - if ((nodeN != null) && (nodeE != null)) - { - if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) - { - addNode(x + 1, y - 1, z, true); - } - } - - // NorthWest - if ((nodeN != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) - { - addNode(x - 1, y - 1, z, true); - } - } - } - - private 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(); - // reinit node if needed - if (result.getLoc() != null) - { - result.getLoc().set(x, y, z); - } - else - { - result.setLoc(new NodeLoc(x, y, z)); - } - } - - return result; - } - - private 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 boolean isHighWeight(int x, int y, int z) - { - final CellNode result = getNode(x, y, z); - if (result == null) - { - return true; - } - - if (!result.getLoc().canGoAll()) - { - return true; - } - if (Math.abs(result.getLoc().getZ() - z) > 16) - { - return true; - } - - return false; - } - - private 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java new file mode 100644 index 0000000000..fa5ca43c2f --- /dev/null +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -0,0 +1,111 @@ +/* + * 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 . + */ +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 geodata values. + private int _geoX; + private int _geoY; + private byte _nswe; + + // 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; + + _costG = 0; + _costH = 0; + _costF = 0; + + _parent = null; + } + + public void setGeo(int gx, int gy, int gz, byte nswe) + { + super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); + + _geoX = gx; + _geoY = gy; + _nswe = nswe; + } + + 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 int getCostF() + { + return _costF; + } + + public Node getParent() + { + return _parent; + } + + @Override + public int compareTo(Node o) + { + return _costF - o._costF; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java new file mode 100644 index 0000000000..b464212c96 --- /dev/null +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -0,0 +1,368 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.awt.Color; +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; +import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; + +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 _opened; + // Container holding Nodes already explored. + private final List _closed; + + // Target coordinates. + private int _gtx; + private int _gty; + private int _gtz; + + // Pathfinding statistics. + private long _timeStamp; + private long _lastElapsedTime; + + 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 findPath(int gox, int goy, int goz, int gtx, int gty, int gtz) + { + // Set start timestamp. + _timeStamp = System.currentTimeMillis(); + + // 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)); + _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 constructPath() + { + // Create result. + final LinkedList 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; + } + + /** + * Creates list of Nodes to show debug path. + * @param debug : The debug packet to add debug informations in. + */ + public void debugPath(ExServerPrimitive debug) + { + // Add all opened node as yellow points. + for (Node n : _opened) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); + } + + // Add all opened node as blue points. + for (Node n : _closed) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); + } + } + + 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; + + _lastElapsedTime = System.currentTimeMillis() - _timeStamp; + _lock.unlock(); + } + + public long getElapsedTime() + { + return _lastElapsedTime; + } + + /** + * Expand the current {@link Node} by exploring its neighbors (axially and diagonally). + */ + private void expand() + { + // Movement is blocked, skip. + final byte nswe = _current.getNSWE(); + if (nswe == 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; + + // Can move north, expand. + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + } + + // Can move south, expand. + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + } + + // Can move west, expand. + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move east, expand. + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move north-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move north-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + } + + /** + * 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 weight : The weight of movement to the new node. + * @return The nswe of the added node. Blank, if not added. + */ + private byte addNode(int gx, int gy, int gzValue, int weight) + { + // 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]; + + // Set node geodata coordinates. + node.setGeo(gx, gy, gz, nswe); + + // 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, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + _opened.add(node); + _bufferIndex++; + return nswe; + } + + /** + * Calculate cost H value, calculated using diagonal distance method.
+ * 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. + final int dx = Math.abs(gx - _gtx); + final int dy = Math.abs(gy - _gty); + final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + + // Get diagonal and axial differences to the target. + final int dd = Math.min(dx, dy); + final int da = Math.max(dx, dy) - dd; + + // Calculate the diagonal distance of the node to the target. + return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + } +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java deleted file mode 100644 index e3bad04b25..0000000000 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; - -/** - * @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 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 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; - // return super.hashCode(); - } - - @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; - if (_x != other._x) - { - return false; - } - if (_y != other._y) - { - return false; - } - if (_goNorth != other._goNorth) - { - return false; - } - if (_goEast != other._goEast) - { - return false; - } - if (_goSouth != other._goSouth) - { - return false; - } - if (_goWest != other._goWest) - { - return false; - } - if (_geoHeight != other._geoHeight) - { - return false; - } - return true; - } -} diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java index fec7e24f5e..c93649d8fd 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java @@ -94,15 +94,15 @@ public class ZoneManager implements IXmlReader private static final Map SETTINGS = new HashMap<>(); private static final int SHIFT_BY = 15; - private static final int OFFSET_X = Math.abs(World.MAP_MIN_X >> SHIFT_BY); - private static final int OFFSET_Y = Math.abs(World.MAP_MIN_Y >> SHIFT_BY); + private static final int OFFSET_X = Math.abs(World.WORLD_X_MIN >> SHIFT_BY); + private static final int OFFSET_Y = Math.abs(World.WORLD_Y_MIN >> SHIFT_BY); private final Map, ConcurrentHashMap> _classZones = new ConcurrentHashMap<>(); private final Map _spawnTerritories = new ConcurrentHashMap<>(); private final AtomicInteger _lastDynamicId = new AtomicInteger(300000); private List _debugItems; - private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.MAP_MAX_X >> SHIFT_BY) + OFFSET_X + 1][(World.MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y + 1]; + private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.WORLD_X_MAX >> SHIFT_BY) + OFFSET_X + 1][(World.WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y + 1]; /** * Instantiates a new zone manager. diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/Location.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/Location.java index 73689ab1a6..ca3b22a3f0 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/Location.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/Location.java @@ -16,30 +16,30 @@ */ package org.l2jmobius.gameserver.model; +import java.util.Objects; + +import org.l2jmobius.commons.util.Point2D; import org.l2jmobius.gameserver.model.interfaces.ILocational; import org.l2jmobius.gameserver.model.interfaces.IPositionable; /** - * Location data transfer object.
- * Contains coordinates data, heading and instance Id. - * @author Zoey76 + * A datatype used to retain a 3D (x/y/z/heading) point. It got the capability to be set and cleaned. */ -public class Location implements IPositionable +public class Location extends Point2D implements IPositionable { - protected int _x; - protected int _y; - protected int _z; - private int _heading; + protected volatile int _z; + protected volatile int _heading; public Location(int x, int y, int z) { - this(x, y, z, 0); + super(x, y); + _z = z; + _heading = 0; } public Location(int x, int y, int z, int heading) { - _x = x; - _y = y; + super(x, y); _z = z; _heading = heading; } @@ -51,9 +51,8 @@ public class Location implements IPositionable public Location(StatSet set) { - _x = set.getInt("x"); - _y = set.getInt("y"); - _z = set.getInt("z"); + super(set.getInt("x", 0), set.getInt("y", 0)); + _z = set.getInt("z", 0); _heading = set.getInt("heading", 0); } @@ -146,6 +145,25 @@ public class Location implements IPositionable _heading = loc.getHeading(); } + @Override + public void clean() + { + super.clean(); + _z = 0; + } + + @Override + public Location clone() + { + return new Location(_x, _y, _z); + } + + @Override + public int hashCode() + { + return (31 * super.hashCode()) + Objects.hash(_z); + } + @Override public boolean equals(Object obj) { diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/World.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/World.java index c1510e8c0e..0d796dd834 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/World.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/World.java @@ -66,19 +66,19 @@ public class World public static final int TILE_Y_MAX = 26; public static final int TILE_ZERO_COORD_X = 20; public static final int TILE_ZERO_COORD_Y = 18; - public static final int MAP_MIN_X = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; - public static final int MAP_MIN_Y = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; + public static final int WORLD_X_MIN = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; + public static final int WORLD_Y_MIN = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; - public static final int MAP_MAX_X = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; - public static final int MAP_MAX_Y = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; + public static final int WORLD_X_MAX = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; + public static final int WORLD_Y_MAX = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; /** Calculated offset used so top left region is 0,0 */ - public static final int OFFSET_X = Math.abs(MAP_MIN_X >> SHIFT_BY); - public static final int OFFSET_Y = Math.abs(MAP_MIN_Y >> SHIFT_BY); + public static final int OFFSET_X = Math.abs(WORLD_X_MIN >> SHIFT_BY); + public static final int OFFSET_Y = Math.abs(WORLD_Y_MIN >> SHIFT_BY); /** Number of regions. */ - private static final int REGIONS_X = (MAP_MAX_X >> SHIFT_BY) + OFFSET_X; - private static final int REGIONS_Y = (MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y; + private static final int REGIONS_X = (WORLD_X_MAX >> SHIFT_BY) + OFFSET_X; + private static final int REGIONS_Y = (WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y; /** Map containing all the players in game. */ private static final Map _allPlayers = new ConcurrentHashMap<>(); @@ -817,9 +817,6 @@ public class World return _memberInPartyNumber.get(); } - /** - * @return the current instance of World - */ public static World getInstance() { return SingletonHolder.INSTANCE; diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/WorldObject.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/WorldObject.java index 0e5536bbc0..62313ffea1 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/WorldObject.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/WorldObject.java @@ -188,23 +188,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif synchronized (this) { int spawnX = x; - if (spawnX > World.MAP_MAX_X) + if (spawnX > World.WORLD_X_MAX) { - spawnX = World.MAP_MAX_X - 5000; + spawnX = World.WORLD_X_MAX - 5000; } - if (spawnX < World.MAP_MIN_X) + if (spawnX < World.WORLD_X_MIN) { - spawnX = World.MAP_MIN_X + 5000; + spawnX = World.WORLD_X_MIN + 5000; } int spawnY = y; - if (spawnY > World.MAP_MAX_Y) + if (spawnY > World.WORLD_Y_MAX) { - spawnY = World.MAP_MAX_Y - 5000; + spawnY = World.WORLD_Y_MAX - 5000; } - if (spawnY < World.MAP_MIN_Y) + if (spawnY < World.WORLD_Y_MIN) { - spawnY = World.MAP_MIN_Y + 5000; + spawnY = World.WORLD_Y_MIN + 5000; } // Set the x,y,z position of the WorldObject. If flagged with _isSpawned, setXYZ will automatically update world region, so avoid that. @@ -518,23 +518,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif public void setXYZInvisible(int x, int y, int z) { int correctX = x; - if (correctX > World.MAP_MAX_X) + if (correctX > World.WORLD_X_MAX) { - correctX = World.MAP_MAX_X - 5000; + correctX = World.WORLD_X_MAX - 5000; } - if (correctX < World.MAP_MIN_X) + if (correctX < World.WORLD_X_MIN) { - correctX = World.MAP_MIN_X + 5000; + correctX = World.WORLD_X_MIN + 5000; } int correctY = y; - if (correctY > World.MAP_MAX_Y) + if (correctY > World.WORLD_Y_MAX) { - correctY = World.MAP_MAX_Y - 5000; + correctY = World.WORLD_Y_MAX - 5000; } - if (correctY < World.MAP_MIN_Y) + if (correctY < World.WORLD_Y_MIN) { - correctY = World.MAP_MIN_Y + 5000; + correctY = World.WORLD_Y_MIN + 5000; } setXYZ(correctX, correctY, z); diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/Creature.java index 53f5416ecc..0a37d8153a 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -66,8 +66,6 @@ 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.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -2589,7 +2587,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3421,8 +3419,8 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe final int originalX = x; final int originalY = y; final int originalZ = z; - final int gtx = (originalX - World.MAP_MIN_X) >> 4; - final int gty = (originalY - World.MAP_MIN_Y) >> 4; + final int gtx = (originalX - World.WORLD_X_MIN) >> 4; + final int gty = (originalY - World.WORLD_Y_MIN) >> 4; if (isOnGeodataPath()) { try @@ -3445,7 +3443,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe && !(((curZ - z) > 300) && (distance < 300))) // Prohibit correcting destination if character wants to fall. { // location different if destination wasn't reached (or just z coord is different) - final Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(curX, curY, curZ, x, y, z, getInstanceWorld()); + final Location destiny = GeoEngine.getInstance().getValidLocation(curX, curY, curZ, x, y, z, getInstanceWorld()); x = destiny.getX(); y = destiny.getY(); z = destiny.getZ(); @@ -3459,7 +3457,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { if (isPlayer() && !_isFlying && !isInWater) diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/Summon.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/Summon.java index e305cdc381..b728a4d008 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/Summon.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/Summon.java @@ -106,7 +106,7 @@ public abstract class Summon extends Playable final int x = owner.getX(); final int y = owner.getY(); final int z = owner.getZ(); - final Location location = GeoEngine.getInstance().canMoveToTargetLoc(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, owner.getInstanceWorld()); + final Location location = GeoEngine.getInstance().getValidLocation(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, getInstanceWorld()); setXYZInvisible(location.getX(), location.getY(), location.getZ()); } diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index 4d59beb38b..07e98246de 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -14381,8 +14381,8 @@ public class PlayerInstance extends Playable public boolean isInTimedHuntingZone(int zoneId, int locX, int locY) { - final int x = ((locX - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((locY - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((locX - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((locY - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; switch (zoneId) { diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java index abaec9036f..20cd1966d5 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java @@ -1560,7 +1560,7 @@ public class ItemInstance extends WorldObject if (dropper != null) { final Instance instance = dropper.getInstanceWorld(); - final Location dropDest = GeoEngine.getInstance().canMoveToTargetLoc(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); + final Location dropDest = GeoEngine.getInstance().getValidLocation(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); x = dropDest.getX(); y = dropDest.getY(); z = dropDest.getZ(); diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java index 44db75b858..02113758c2 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java @@ -1184,7 +1184,7 @@ public class SkillCaster implements Runnable } } - final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().canMoveToTargetLoc(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); + final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().getValidLocation(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); creature.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); creature.broadcastPacket(new FlyToLocation(creature, destination, flyType, 0, 0, 333)); diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java index c62c821f44..30068134e3 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java @@ -114,7 +114,7 @@ public class ValidatePosition implements IClientIncomingPacket { if (player.isFalling(_z)) { - final int nearestZ = GeoEngine.getInstance().getHigherHeight(_x, _y, _z); + final int nearestZ = GeoEngine.getInstance().getHeight(_x, _y, _z); if (player.getZ() < nearestZ) { player.setXYZ(_x, _y, nearestZ); diff --git a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/util/GeoUtils.java index 4600a0fc53..19a979819e 100644 --- a/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_Essence_4.0_DwellingOfSpirits/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -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.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; @@ -30,21 +30,21 @@ public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getInstance().getWorldX(iter.x()); - final int wy = GeoEngine.getInstance().getWorldY(iter.y()); + final int wx = GeoEngine.getWorldX(iter.x()); + final int wy = GeoEngine.getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public final class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); - int wy = GeoEngine.getInstance().getWorldY(prevY); + int wx = GeoEngine.getWorldX(prevX); + int wy = GeoEngine.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.getInstance().getWorldX(curX); - wy = GeoEngine.getInstance().getWorldY(curY); + wx = GeoEngine.getWorldX(curX); + wy = GeoEngine.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().checkNearestNswe(x, y, z, nswe)) + if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0) { return Color.GREEN; } @@ -109,9 +109,8 @@ public final class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final GeoEngine ge = GeoEngine.getInstance(); - final int playerGx = ge.getGeoX(player.getX()); - final int playerGy = ge.getGeoY(player.getY()); + final int playerGx = GeoEngine.getGeoX(player.getX()); + final int playerGy = GeoEngine.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -135,32 +134,32 @@ public final class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = ge.getWorldX(gx); - final int y = ge.getWorldY(gy); - final int z = ge.getNearestZ(gx, gy, player.getZ()); + final int x = GeoEngine.getWorldX(gx); + final int y = GeoEngine.getWorldY(gy); + final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); + Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); 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, Cell.NSWE_EAST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); 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, Cell.NSWE_SOUTH); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); 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, Cell.NSWE_WEST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); 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); @@ -188,41 +187,41 @@ public final class GeoUtils { if (y > lastY) { - return Cell.NSWE_SOUTH_EAST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E; } else if (y < lastY) { - return Cell.NSWE_NORTH_EAST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E; } else { - return Cell.NSWE_EAST; + return GeoStructure.CELL_FLAG_E; } } else if (x < lastX) // west { if (y > lastY) { - return Cell.NSWE_SOUTH_WEST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W; } else if (y < lastY) { - return Cell.NSWE_NORTH_WEST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W; } else { - return Cell.NSWE_WEST; + return GeoStructure.CELL_FLAG_W; } } else // unchanged x { if (y > lastY) { - return Cell.NSWE_SOUTH; + return GeoStructure.CELL_FLAG_S; } else if (y < lastY) { - return Cell.NSWE_NORTH; + return GeoStructure.CELL_FLAG_N; } else { diff --git a/L2J_Mobius_Essence_5.0_Sylph/dist/game/config/GeoEngine.ini b/L2J_Mobius_Essence_5.0_Sylph/dist/game/config/GeoEngine.ini index 609e8a89a7..e740c678dd 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/dist/game/config/GeoEngine.ini +++ b/L2J_Mobius_Essence_5.0_Sylph/dist/game/config/GeoEngine.ini @@ -6,33 +6,44 @@ # at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/ 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 + # ================================================================= -# Path finding +# Pathfinding # ================================================================= # 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 +# an alternative path (e.g. walk around obstacle), default: True +PathFinding = True -# Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 -PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 +# Pathfinding array buffers configuration, default: 500x10;1000x10;3000x5;5000x3;10000x3 +PathFindBuffers = 500x10;1000x10;3000x5;5000x3;10000x3 -# Weight for nodes without obstacles far from walls -LowWeight = 0.5 +# Movement weight, when moving from one to another axially and diagonally, default: 10 and 14 +MoveWeight = 10 +MoveWeightDiag = 14 -# Weight for nodes near walls -MediumWeight = 2 +# 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 -# Weight for nodes with obstacles -HighWeight = 3 +# 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. -# Default: LowWeight * sqrt(2) -DiagonalWeight = 0.707 +# Maximum number of generated nodes per one path-finding process, default 3500 +MaxIterations = 3500 # ================================================================= -# Other +# Line of Sight # ================================================================= -# Correct player Z after falling through the ground. -CorrectPlayerZ = False +# 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 diff --git a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/geodata/L2D_GeoEngine.patch b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/geodata/L2D_GeoEngine.patch deleted file mode 100644 index f8577e3a0e..0000000000 --- a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/geodata/L2D_GeoEngine.patch +++ /dev/null @@ -1,6035 +0,0 @@ -Index: dist/game/GeoDataConverter.bat -=================================================================== ---- dist/game/GeoDataConverter.bat (nonexistent) -+++ dist/game/GeoDataConverter.bat (working copy) -@@ -0,0 +1,6 @@ -+@echo off -+title L2D geodata converter -+ -+java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter -+ -+pause -Index: dist/game/GeoDataConverter.sh -=================================================================== ---- dist/game/GeoDataConverter.sh (nonexistent) -+++ dist/game/GeoDataConverter.sh (working copy) -@@ -0,0 +1,4 @@ -+#! /bin/sh -+ -+java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1 -+ -Index: dist/game/config/GeoEngine.ini -=================================================================== ---- dist/game/config/GeoEngine.ini (revision 8409) -+++ dist/game/config/GeoEngine.ini (working copy) -@@ -1,6 +1,10 @@ - # ================================================================= - # Geodata - # ================================================================= -+# Because of real-time performance we are using geodata files only in -+# diagonal L2D format now (using filename e.g. 22_16.l2d). -+# L2D geodata can be obtained by conversion of existing L2J or L2OFF geodata. -+# Launch "GeoDataConverter.bat/sh" and follow instructions to start the conversion. - - # 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/ -@@ -13,6 +17,16 @@ - CoordSynchronize = 2 - - # ================================================================= -+# Path checking -+# ================================================================= -+ -+# 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 finding - # ================================================================= - -@@ -23,19 +37,23 @@ - # Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2 - --# Weight for nodes without obstacles far from walls --LowWeight = 0.5 -+# Base path weight, when moving from one node to another on axis direction, default: 10 -+BaseWeight = 10 - --# Weight for nodes near walls --MediumWeight = 2 -+# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14 -+DiagonalWeight = 14 - --# Weight for nodes with obstacles --HighWeight = 3 -+# When movement flags of target node is blocked to any direction, multiply movement weight by this multiplier. -+# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 10 -+ObstacleMultiplier = 10 - --# Weight for diagonal movement. --# Default: LowWeight * sqrt(2) --DiagonalWeight = 0.707 -+# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 20 -+# For proper function must be higher than BaseWeight and/or DiagonalWeight. -+HeuristicWeight = 20 - -+# Maximum number of generated nodes per one path-finding process, default 3500 -+MaxIterations = 3500 -+ - # ================================================================= - # Other - # ================================================================= -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java (working copy) -@@ -55,9 +55,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -@@ -73,9 +72,8 @@ - final int worldX = activeChar.getX(); - final int worldY = activeChar.getY(); - final int worldZ = activeChar.getZ(); -- final int geoX = GeoEngine.getInstance().getGeoX(worldX); -- final int geoY = GeoEngine.getInstance().getGeoY(worldY); -- -+ final int geoX = GeoEngine.getGeoX(worldX); -+ final int geoY = GeoEngine.getGeoY(worldY); - if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) - { - BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ)); -Index: dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java -=================================================================== ---- dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (revision 8409) -+++ dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java (working copy) -@@ -18,11 +18,11 @@ - - import java.util.List; - --import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -+import org.l2jmobius.gameserver.geoengine.GeoEngine; - import org.l2jmobius.gameserver.handler.IAdminCommandHandler; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -+import org.l2jmobius.gameserver.network.SystemMessageId; - import org.l2jmobius.gameserver.util.BuilderUtil; - - public class AdminPathNode implements IAdminCommandHandler -@@ -37,29 +37,30 @@ - { - if (command.equals("admin_path_find")) - { -- if (!Config.PATHFINDING) -- { -- BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled."); -- return true; -- } - if (activeChar.getTarget() != null) - { -- final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); -+ final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); - if (path == null) - { -- BuilderUtil.sendSysMessage(activeChar, "No Route!"); -- return true; -+ BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled."); - } -- for (AbstractNodeLoc a : path) -+ else - { -- BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); -+ for (Location point : path) -+ { -+ BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ()); -+ } - } - } - else - { -- BuilderUtil.sendSysMessage(activeChar, "No Target!"); -+ activeChar.sendPacket(SystemMessageId.INVALID_TARGET); - } - } -+ else -+ { -+ return false; -+ } - return true; - } - -Index: java/org/l2jmobius/Config.java -=================================================================== ---- java/org/l2jmobius/Config.java (revision 8409) -+++ java/org/l2jmobius/Config.java (working copy) -@@ -32,7 +32,6 @@ - import java.net.UnknownHostException; - import java.nio.charset.StandardCharsets; - import java.nio.file.Files; --import java.nio.file.Path; - import java.nio.file.Paths; - import java.time.Duration; - import java.util.ArrayList; -@@ -966,14 +965,17 @@ - // -------------------------------------------------- - // GeoEngine - // -------------------------------------------------- -- public static Path GEODATA_PATH; -+ public static String GEODATA_PATH; -+ public static int COORD_SYNCHRONIZE; -+ public static int PART_OF_CHARACTER_HEIGHT; -+ public static int MAX_OBSTACLE_HEIGHT; - public static boolean PATHFINDING; - public static String PATHFIND_BUFFERS; -- public static float LOW_WEIGHT; -- public static float MEDIUM_WEIGHT; -- public static float HIGH_WEIGHT; -- public static float DIAGONAL_WEIGHT; -- public static int COORD_SYNCHRONIZE; -+ public static int BASE_WEIGHT; -+ public static int DIAGONAL_WEIGHT; -+ public static int HEURISTIC_WEIGHT; -+ public static int OBSTACLE_MULTIPLIER; -+ public static int MAX_ITERATIONS; - public static boolean CORRECT_PLAYER_Z; - - /** Attribute System */ -@@ -2568,14 +2570,17 @@ - - // Load GeoEngine config file (if exists) - final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); -- GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); -+ GEODATA_PATH = GeoEngine.getString("GeoDataPath", "./data/geodata/"); -+ COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); -+ MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); - PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -- LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); -- MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); -- HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); -- DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); -- COORD_SYNCHRONIZE = GeoEngine.getInt("CoordSynchronize", -1); -+ BASE_WEIGHT = GeoEngine.getInt("BaseWeight", 10); -+ DIAGONAL_WEIGHT = GeoEngine.getInt("DiagonalWeight", 14); -+ OBSTACLE_MULTIPLIER = GeoEngine.getInt("ObstacleMultiplier", 10); -+ HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 20); -+ MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); - CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false); - - // Load AllowedPlayerRaces config file (if exists) -Index: java/org/l2jmobius/gameserver/geoengine/GeoEngine.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (revision 8435) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEngine.java (working copy) -@@ -16,100 +16,90 @@ - */ - package org.l2jmobius.gameserver.geoengine; - --import java.io.IOException; -+import java.io.File; - import java.io.RandomAccessFile; - import java.nio.ByteOrder; --import java.nio.channels.FileChannel.MapMode; --import java.nio.file.Files; --import java.nio.file.Path; --import java.util.concurrent.atomic.AtomicReferenceArray; --import java.util.logging.Level; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.List; - import java.util.logging.Logger; - - import org.l2jmobius.Config; - import org.l2jmobius.gameserver.data.xml.DoorData; - import org.l2jmobius.gameserver.data.xml.FenceData; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; --import org.l2jmobius.gameserver.geoengine.geodata.IRegion; --import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; --import org.l2jmobius.gameserver.geoengine.geodata.Region; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.World; - import org.l2jmobius.gameserver.model.WorldObject; -+import org.l2jmobius.gameserver.model.actor.Creature; - import org.l2jmobius.gameserver.model.instancezone.Instance; --import org.l2jmobius.gameserver.model.interfaces.ILocational; --import org.l2jmobius.gameserver.util.GeoUtils; --import org.l2jmobius.gameserver.util.LinePointIterator; --import org.l2jmobius.gameserver.util.LinePointIterator3D; -+import org.l2jmobius.gameserver.util.MathUtil; - - /** -- * @author -Nemesiss-, HorridoJoho -+ * @author Hasha - */ - public class GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); -+ protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - -- private static final int WORLD_MIN_X = -655360; -- private static final int WORLD_MIN_Y = -589824; -- private static final int WORLD_MIN_Z = -16384; -+ private final ABlock[][] _blocks; -+ private final BlockNull _nullBlock; - -- /** 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; -- -- /** The regions array */ -- private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); -- -- private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; -- private static final int ELEVATED_SEE_OVER_DISTANCE = 2; -- private static final int MAX_SEE_OVER_HEIGHT = 48; -- -- protected GeoEngine() -+ /** -+ * GeoEngine constructor. Loads all geodata files of chosen geodata format. -+ */ -+ public GeoEngine() - { - LOGGER.info("GeoEngine: Initializing..."); -- for (int i = 0; i < _regions.length(); i++) -- { -- _regions.set(i, NullRegion.INSTANCE); -- } - -+ // initialize block container -+ _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; -+ -+ // load null block -+ _nullBlock = new BlockNull(); -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files according to geoengine config setup - int loaded = 0; -- try -+ long fileSize = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) - { -- for (int regionX = World.TILE_X_MIN; regionX <= World.TILE_X_MAX; regionX++) -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) - { -- for (int regionY = World.TILE_Y_MIN; regionY <= World.TILE_Y_MAX; regionY++) -+ final File f = new File(Config.GEODATA_PATH + String.format(GeoFormat.L2D.getFilename(), rx, ry)); -+ if (f.exists() && !f.isDirectory()) - { -- final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); -- if (Files.exists(geoFilePath)) -+ // region file is load-able, try to load it -+ if (loadGeoBlocks(rx, ry)) - { -- try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) -- { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -- loaded++; -- } -+ loaded++; -+ fileSize += f.length(); - } - } -+ else -+ { -+ // region file is not load-able, load null blocks -+ loadNullBlocks(rx, ry); -+ } - } - } -- catch (Exception e) -+ -+ LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -+ if (loaded > 0) - { -- LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); -- System.exit(1); -+ LOGGER.info("GeoEngine: Total geodata file size " + (fileSize / 1024 / 1024) + " MB."); - } - -- LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); -- -- // Avoid wrong configs when no files are loaded. -+ // avoid wrong configs when no files are loaded - if (loaded == 0) - { - if (Config.PATHFINDING) -@@ -123,619 +113,820 @@ - LOGGER.info("GeoEngine: Forcing CoordSynchronize setting to -1."); - } - } -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); - } - - /** -- * @param geoX -- * @param geoY -- * @return the region -+ * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. -+ * @return boolean : True, when geodata file was loaded without problem. - */ -- private IRegion getRegion(int geoX, int geoY) -+ private final boolean loadGeoBlocks(int regionX, int regionY) - { -- final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); -- if ((region < 0) || (region >= _regions.length())) -+ final String filename = String.format(GeoFormat.L2D.getFilename(), regionX, regionY); -+ -+ // standard load -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) - { -- return null; -+ // initialize file buffer -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer, GeoFormat.L2D); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2D: -+ { -+ _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, GeoFormat.L2D); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ } -+ -+ // check data consistency -+ if (buffer.remaining() > 0) -+ { -+ LOGGER.warning("GeoEngine: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ } -+ -+ // loading was successful -+ return true; - } -- return _regions.get(region); -- } -- -- /** -- * @param filePath -- * @param regionX -- * @param regionY -- * @throws IOException -- */ -- 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")) -+ catch (Exception e) - { -- _regions.set(regionOffset, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); -+ // an error occured while loading, load null blocks -+ LOGGER.warning("GeoEngine: Error while loading " + filename + " region file."); -+ LOGGER.warning(e.getMessage()); -+ -+ // replace whole region file with null blocks -+ loadNullBlocks(regionX, regionY); -+ -+ // loading was not successful -+ return false; - } - } - - /** -- * @param regionX -- * @param regionY -+ * Loads null blocks. Used when no region file is detected or an error occurs during loading. -+ * @param regionX : Geodata file region X coordinate. -+ * @param regionY : Geodata file region Y coordinate. - */ -- public void unloadRegion(int regionX, int regionY) -+ private final void loadNullBlocks(int regionX, int regionY) - { -- _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); -- } -- -- /** -- * @param geoX -- * @param geoY -- * @return if geodata exist -- */ -- public boolean hasGeoPos(int geoX, int geoY) -- { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ // get block indexes -+ final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; -+ final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; -+ -+ // load all null blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) - { -- return false; -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[blockX + ix][blockY + iy] = _nullBlock; -+ } - } -- return region.hasGeo(); - } - -+ // GEODATA - GENERAL -+ - /** -- * Checks the specified position for available geodata. -- * @param x the world x -- * @param y the world y -- * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise -+ * Converts world X to geodata X. -+ * @param worldX -+ * @return int : Geo X - */ -- public boolean hasGeo(int x, int y) -+ public static int getGeoX(int worldX) - { -- return hasGeoPos(getGeoX(x), getGeoY(y)); -+ return (MathUtil.limit(worldX, World.MAP_MIN_X, World.MAP_MAX_X) - World.MAP_MIN_X) >> 4; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe check -+ * Converts world Y to geodata Y. -+ * @param worldY -+ * @return int : Geo Y - */ -- public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) -+ public static int getGeoY(int worldY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return true; -- } -- return region.checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(worldY, World.MAP_MIN_Y, World.MAP_MAX_Y) - World.MAP_MIN_Y) >> 4; - } - - /** -+ * Converts geodata X to world X. - * @param geoX -- * @param geoY -- * @param worldZ -- * @param nswe -- * @return the nearest nswe anti-corner cut -+ * @return int : World X - */ -- public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) -+ public static int getWorldX(int geoX) - { -- boolean can = true; -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) -- { -- can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); -- } -- return can && checkNearestNswe(geoX, geoY, worldZ, nswe); -+ return (MathUtil.limit(geoX, 0, GeoStructure.GEO_CELLS_X) << 4) + World.MAP_MIN_X + 8; - } - - /** -- * @param geoX -+ * Converts geodata Y to world Y. - * @param geoY -- * @param worldZ -- * @return the nearest Z value -+ * @return int : World Y - */ -- public int getNearestZ(int geoX, int geoY, int worldZ) -+ public static int getWorldY(int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -- { -- return worldZ; -- } -- return region.getNearestZ(geoX, geoY, worldZ); -+ return (MathUtil.limit(geoY, 0, GeoStructure.GEO_CELLS_Y) << 4) + World.MAP_MIN_Y + 8; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next lower Z value -+ * Returns block of geodata on given coordinates. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return {@link ABlock} : Block of geodata. - */ -- public int getNextLowerZ(int geoX, int geoY, int worldZ) -+ private final ABlock getBlock(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final int x = geoX / GeoStructure.BLOCK_CELLS_X; -+ final int y = geoY / GeoStructure.BLOCK_CELLS_Y; -+ -+ // if x or y is out of array return null -+ if ((x > -1) && (y > -1) && (x < GeoStructure.GEO_BLOCKS_X) && (y < GeoStructure.GEO_BLOCKS_Y)) - { -- return worldZ; -+ return _blocks[x][y]; - } -- return region.getNextLowerZ(geoX, geoY, worldZ); -+ return null; - } - - /** -- * @param geoX -- * @param geoY -- * @param worldZ -- * @return the next higher Z value -+ * Check if geo coordinates has geo. -+ * @param geoX : Geodata X -+ * @param geoY : Geodata Y -+ * @return boolean : True, if given geo coordinates have geodata - */ -- public int getNextHigherZ(int geoX, int geoY, int worldZ) -+ public boolean hasGeoPos(int geoX, int geoY) - { -- final IRegion region = getRegion(geoX, geoY); -- if (region == null) -+ final ABlock block = getBlock(geoX, geoY); -+ if (block == null) // null block check - { -- return worldZ; -+ // TODO: Find when this can be null. (Bad geodata? Check World getRegion method.) -+ // LOGGER.warning("Could not find geodata block at " + getWorldX(geoX) + ", " + getWorldY(geoY) + "."); -+ return false; - } -- return region.getNextHigherZ(geoX, geoY, worldZ); -+ return block.hasGeoPos(); - } - - /** -- * Gets the Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the height of cell, which is closest to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, closest to given coordinates. - */ -- public int getHeight(int x, int y, int z) -+ public short getHeightNearest(int geoX, int geoY, int worldZ) - { -- return getNearestZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getHeightNearest(geoX, geoY, worldZ) : (short) worldZ; - } - - /** -- * Gets the next lower Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Returns the NSWE flag byte of cell, which is closes to given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. - */ -- public int getLowerHeight(int x, int y, int z) -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) - { -- return getNextLowerZ(getGeoX(x), getGeoY(y), z); -+ final ABlock block = getBlock(geoX, geoY); -+ return block != null ? block.getNsweNearest(geoX, geoY, worldZ) : (byte) 0xFF; - } - - /** -- * Gets the next higher Z height. -- * @param x the world x -- * @param y the world y -- * @param z the world z -- * @return the nearest Z height -+ * Check if world coordinates has geo. -+ * @param worldX : World X -+ * @param worldY : World Y -+ * @return boolean : True, if given world coordinates have geodata - */ -- public int getHigherHeight(int x, int y, int z) -+ public boolean hasGeo(int worldX, int worldY) - { -- return getNextHigherZ(getGeoX(x), getGeoY(y), z); -+ return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); - } - - /** -- * @param worldX -- * @return the geo X -+ * Returns closest Z coordinate according to geodata. -+ * @param worldX : world x -+ * @param worldY : world y -+ * @param worldZ : world z -+ * @return short : nearest Z coordinates according to geodata - */ -- public int getGeoX(int worldX) -+ public short getHeight(int worldX, int worldY, int worldZ) - { -- return (worldX - WORLD_MIN_X) / 16; -+ return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); - } - -- /** -- * @param worldY -- * @return the geo Y -- */ -- public int getGeoY(int worldY) -- { -- return (worldY - WORLD_MIN_Y) / 16; -- } -+ // PATHFINDING - - /** -- * @param worldZ -- * @return the geo Z -+ * Check line of sight from {@link WorldObject} to {@link WorldObject}. -+ * @param origin : The origin object. -+ * @param target : The target object. -+ * @return {@code boolean} : True if origin can see target - */ -- public int getGeoZ(int worldZ) -+ public boolean canSeeTarget(WorldObject origin, WorldObject target) - { -- return (worldZ - WORLD_MIN_Z) / 16; -- } -- -- /** -- * @param geoX -- * @return the world X -- */ -- public int getWorldX(int geoX) -- { -- return (geoX * 16) + WORLD_MIN_X + 8; -- } -- -- /** -- * @param geoY -- * @return the world Y -- */ -- public int getWorldY(int geoY) -- { -- return (geoY * 16) + WORLD_MIN_Y + 8; -- } -- -- /** -- * @param geoZ -- * @return the world Z -- */ -- public int getWorldZ(int geoZ) -- { -- return (geoZ * 16) + WORLD_MIN_Z + 8; -- } -- -- /** -- * Can see target. Doors as target always return true. Checks doors between. -- * @param cha the character -- * @param target the target -- * @return {@code true} if the character can see the target (LOS), {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, WorldObject target) -- { -- if (target.isDoor()) -+ if (target.isDoor() || target.isArtefact() || (target.isCreature() && ((Creature) target).isFlying())) - { -- // Can always see doors. - return true; - } -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param cha the character -- * @param worldPosition the world position -- * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise -- */ -- public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) -- { -- return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param world -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @param tworld the target's instanceId -- * @return -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) -- { -- return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); -- } -- -- /** -- * Can see target. Checks doors between. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param z the z coordinate -- * @param instance -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tz the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -- */ -- public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) -- { -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) -+ -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = target.getX(); -+ final int ty = target.getY(); -+ final int tz = target.getZ(); -+ -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) - { - return false; - } -- return canSeeTarget(x, y, z, tx, ty, tz); -- } -- -- /** -- * @param prevX -- * @param prevY -- * @param prevGeoZ -- * @param curX -- * @param curY -- * @param nswe -- * @return the LOS Z value -- */ -- private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) -- { -- if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) - { -- throw new RuntimeException("Multiple directions!"); -+ return false; - } - -- if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- return getNearestZ(curX, curY, prevGeoZ); -+ return true; - } - -- return getNextHigherZ(curX, curY, prevGeoZ); -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) -+ { -+ return true; -+ } -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) -+ { -+ return goz == gtz; -+ } -+ -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) -+ { -+ oheight = ((Creature) origin).getCollisionHeight() * 2; -+ } -+ -+ double theight = 0; -+ if (target.isCreature()) -+ { -+ theight = ((Creature) target).getCollisionHeight() * 2; -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, theight, origin.getInstanceWorld()); - } - - /** -- * Can see target. Does not check doors between. -- * @param xValue the x coordinate -- * @param yValue the y coordinate -- * @param zValue the z coordinate -- * @param txValue the target's x coordinate -- * @param tyValue the target's y coordinate -- * @param tzValue the target's z coordinate -- * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise -+ * Check line of sight from {@link WorldObject} to {@link Location}. -+ * @param origin : The origin object. -+ * @param position : The target position. -+ * @return {@code boolean} : True if object can see position - */ -- public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) -+ public boolean canSeeTarget(WorldObject origin, Location position) - { -- int x = xValue; -- int y = yValue; -- int tx = txValue; -- int ty = tyValue; -+ // get origin and target world coordinates -+ final int ox = origin.getX(); -+ final int oy = origin.getY(); -+ final int oz = origin.getZ(); -+ final int tx = position.getX(); -+ final int ty = position.getY(); -+ final int tz = position.getZ(); - -- int geoX = getGeoX(x); -- int geoY = getGeoY(y); -- int tGeoX = getGeoX(tx); -- int tGeoY = getGeoY(ty); -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld(), true)) -+ { -+ return false; -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, origin.getInstanceWorld())) -+ { -+ return false; -+ } - -- int z = getNearestZ(geoX, geoY, zValue); -- int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- // Fastpath. -- if ((geoX == tGeoX) && (geoY == tGeoY)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- if (hasGeoPos(tGeoX, tGeoY)) -- { -- return z == tz; -- } - return true; - } - -- if (tz > z) -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // origin and target coordinates are same -+ if ((gox == gtx) && (goy == gty)) - { -- int tmp = tx; -- tx = x; -- x = tmp; -- -- tmp = ty; -- ty = y; -- y = tmp; -- -- tmp = tz; -- tz = z; -- z = tmp; -- -- tmp = tGeoX; -- tGeoX = geoX; -- geoX = tmp; -- -- tmp = tGeoY; -- tGeoY = geoY; -- geoY = tmp; -+ return goz == gtz; - } - -- final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); -- // First point is guaranteed to be available, skip it, we can always see our own position. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- final int prevZ = pointIter.z(); -- int prevGeoZ = prevZ; -- int ptIndex = 0; -- while (pointIter.next()) -+ // get origin and target height, real height = collision height * 2 -+ double oheight = 0; -+ if (origin.isCreature()) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -+ oheight = ((Creature) origin).getTemplate().getCollisionHeight(); -+ } -+ -+ // perform geodata check -+ return checkSee(gox, goy, goz, oheight, gtx, gty, gtz, 0, origin.getInstanceWorld()); -+ } -+ -+ /** -+ * Simple check for origin to target visibility. -+ * @param goxValue : origin X geodata coordinate -+ * @param goyValue : origin Y geodata coordinate -+ * @param gozValue : origin Z geodata coordinate -+ * @param oheight : origin height (if instance of {@link Character}) -+ * @param gtxValue : target X geodata coordinate -+ * @param gtyValue : target Y geodata coordinate -+ * @param gtzValue : target Z geodata coordinate -+ * @param theight : target height (if instance of {@link Character}) -+ * @param instance -+ * @return {@code boolean} : True, when target can be seen. -+ */ -+ private final boolean checkSee(int goxValue, int goyValue, int gozValue, double oheight, int gtxValue, int gtyValue, int gtzValue, double theight, Instance instance) -+ { -+ int goz = gozValue; -+ int gtz = gtzValue; -+ int gox = goxValue; -+ int goy = goyValue; -+ int gtx = gtxValue; -+ int gty = gtyValue; -+ -+ // get line of sight Z coordinates -+ double losoz = goz + ((oheight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ double lostz = gtz + ((theight * Config.PART_OF_CHARACTER_HEIGHT) / 100); -+ -+ // get X delta and signum -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirox = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; -+ final byte dirtx = sx > 0 ? GeoStructure.CELL_FLAG_W : GeoStructure.CELL_FLAG_E; -+ -+ // get Y delta and signum -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte diroy = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ final byte dirty = sy > 0 ? GeoStructure.CELL_FLAG_N : GeoStructure.CELL_FLAG_S; -+ -+ // get Z delta -+ final int dm = Math.max(dx, dy); -+ final double dz = (lostz - losoz) / dm; -+ -+ // get direction flag for diagonal movement -+ final byte diroxy = getDirXY(dirox, diroy); -+ final byte dirtxy = getDirXY(dirtx, dirty); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte diro; -+ byte dirt; -+ -+ // initialize node values -+ int nox = gox; -+ int noy = goy; -+ int ntx = gtx; -+ int nty = gty; -+ byte nsweo = getNsweNearest(gox, goy, goz); -+ byte nswet = getNsweNearest(gtx, gty, gtz); -+ -+ // loop -+ ABlock block; -+ int index; -+ for (int i = 0; i < ((dm + 1) / 2); i++) -+ { -+ // reset direction flag -+ diro = 0; -+ dirt = 0; - -- if ((curX == prevX) && (curY == prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- continue; -+ // calculate next point XY coordinates -+ d -= dy; -+ d += dx; -+ nox += sx; -+ ntx -= sx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroxy; -+ dirt |= dirtxy; - } -+ else if (e2 > -dy) -+ { -+ // calculate next point X coordinate -+ d -= dy; -+ nox += sx; -+ ntx -= sx; -+ diro |= dirox; -+ dirt |= dirtx; -+ } -+ else if (e2 < dx) -+ { -+ // calculate next point Y coordinate -+ d += dx; -+ noy += sy; -+ nty -= sy; -+ diro |= diroy; -+ dirt |= dirty; -+ } - -- final int beeCurZ = pointIter.z(); -- int curGeoZ = prevGeoZ; -- -- // Check if the position has geodata. -- if (hasGeoPos(curX, curY)) - { -- final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); -- curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); -- int maxHeight; -- if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) -+ // get block of the next cell -+ block = getBlock(nox, noy); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nsweo & diro) == 0) - { -- maxHeight = z + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexAbove(nox, noy, goz - GeoStructure.CELL_IGNORE_HEIGHT); - } - else - { -- maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; -+ index = block.getIndexBelow(nox, noy, goz + GeoStructure.CELL_IGNORE_HEIGHT); - } - -- boolean canSeeThrough = false; -- if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) -+ // layer does not exist, return -+ if (index == -1) - { -- if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) -- { -- final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); -- canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); -- final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); -- } -- else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) -- { -- final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); -- final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); -- canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); -- } -- else -- { -- canSeeThrough = true; -- } -+ return false; - } - -- if (!canSeeThrough) -+ // get layer and next line of sight Z coordinate -+ goz = block.getHeight(index); -+ losoz += dz; -+ -+ // perform line of sight check, return when fails -+ if ((goz - losoz) > Config.MAX_OBSTACLE_HEIGHT) - { - return false; - } -+ -+ // get layer nswe -+ nsweo = block.getNswe(index); - } -+ { -+ // get block of the next cell -+ block = getBlock(ntx, nty); -+ -+ // get index of particular layer, based on movement conditions -+ if ((nswet & dirt) == 0) -+ { -+ index = block.getIndexAbove(ntx, nty, gtz - GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ else -+ { -+ index = block.getIndexBelow(ntx, nty, gtz + GeoStructure.CELL_IGNORE_HEIGHT); -+ } -+ -+ // layer does not exist, return -+ if (index == -1) -+ { -+ return false; -+ } -+ -+ // get layer and next line of sight Z coordinate -+ gtz = block.getHeight(index); -+ lostz -= dz; -+ -+ // perform line of sight check, return when fails -+ if ((gtz - lostz) > Config.MAX_OBSTACLE_HEIGHT) -+ { -+ return false; -+ } -+ -+ // get layer nswe -+ nswet = block.getNswe(index); -+ } - -- prevX = curX; -- prevY = curY; -- prevGeoZ = curGeoZ; -- ++ptIndex; -+ // update coords -+ gox = nox; -+ goy = noy; -+ gtx = ntx; -+ gty = nty; - } - -- return true; -+ // when iteration is completed, compare final Z coordinates -+ return Math.abs(goz - gtz) < (GeoStructure.CELL_HEIGHT * 4); - } - - /** -- * Move check. -- * @param x the x coordinate -- * @param y the y coordinate -- * @param zValue the z coordinate -- * @param tx the target's x coordinate -- * @param ty the target's y coordinate -- * @param tzValue the target's z coordinate -- * @param instance the instance -- * @return the last Location (x,y,z) where player can walk - just before wall -+ * Check movement from coordinates to coordinates. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {code boolean} : True if target coordinates are reachable from origin coordinates - */ -- public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) -+ public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- final int geoX = getGeoX(x); -- final int geoY = getGeoY(y); -- final int z = getNearestZ(geoX, geoY, zValue); -- final int tGeoX = getGeoX(tx); -- final int tGeoY = getGeoY(ty); -- final int tz = getNearestZ(tGeoX, tGeoY, tzValue); -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) -+ { -+ return true; -+ } - -- if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } -- if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) - { -- return new Location(x, y, getHeight(x, y, z)); -+ return true; - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = z; -+ // perform geodata check -+ final GeoLocation loc = checkMove(gox, goy, goz, gtx, gty, gtz, instance); -+ return (loc.getGeoX() == gtx) && (loc.getGeoY() == gty); -+ } -+ -+ /** -+ * Check movement from origin to target. Returns last available point in the checked path. -+ * @param ox : origin X coordinate -+ * @param oy : origin Y coordinate -+ * @param oz : origin Z coordinate -+ * @param tx : target X coordinate -+ * @param ty : target Y coordinate -+ * @param tz : target Z coordinate -+ * @param instance -+ * @return {@link Location} : Last point where object can walk (just before wall) -+ */ -+ public Location canMoveToTargetLoc(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ // Mobius: Double check for doors before normal checkMove to avoid exploiting key movement. -+ if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) -+ { -+ return new Location(ox, oy, oz); -+ } -+ if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) -+ { -+ return new Location(ox, oy, oz); -+ } - -- while (pointIter.next()) -+ // get origin and check existing geo coordinates -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -- -- if (hasGeoPos(prevX, prevY)) -- { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -- { -- // Can't move, return previous location. -- return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); -- } -- } -- -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return new Location(tx, ty, tz); - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != tz)) -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coordinates -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { -- // Different floors, return start location. -- return new Location(x, y, z); -+ return new Location(tx, ty, tz); - } - -- return new Location(tx, ty, tz); -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // target coordinates reached -+ if ((gox == gtx) && (goy == gty) && (goz == gtz)) -+ { -+ return new Location(tx, ty, tz); -+ } -+ -+ // perform geodata check -+ return checkMove(gox, goy, goz, gtx, gty, gtz, instance); - } - - /** -- * Checks if its possible to move from one location to another. -- * @param fromX the X coordinate to start checking from -- * @param fromY the Y coordinate to start checking from -- * @param fromZvalue the Z coordinate to start checking from -- * @param toX the X coordinate to end checking at -- * @param toY the Y coordinate to end checking at -- * @param toZvalue the Z coordinate to end checking at -- * @param instance the instance -- * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise -+ * With this method you can check if a position is visible or can be reached by beeline movement.
-+ * Target X and Y reachable and Z is on same floor: -+ *
    -+ *
  • Location of the target with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y reachable but Z is on another floor: -+ *
    -+ *
  • Location of the origin with corrected Z value from geodata.
  • -+ *
-+ * Target X and Y not reachable: -+ *
    -+ *
  • Last accessible location in destination to target.
  • -+ *
-+ * @param gox : origin X geodata coordinate -+ * @param goy : origin Y geodata coordinate -+ * @param goz : origin Z geodata coordinate -+ * @param gtx : target X geodata coordinate -+ * @param gty : target Y geodata coordinate -+ * @param gtz : target Z geodata coordinate -+ * @param instance -+ * @return {@link GeoLocation} : The last allowed point of movement. - */ -- public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) -+ protected final GeoLocation checkMove(int gox, int goy, int goz, int gtx, int gty, int gtz, Instance instance) - { -- final int geoX = getGeoX(fromX); -- final int geoY = getGeoY(fromY); -- final int fromZ = getNearestZ(geoX, geoY, fromZvalue); -- final int tGeoX = getGeoX(toX); -- final int tGeoY = getGeoY(toY); -- final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); -- -- if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) -+ if (DoorData.getInstance().checkIfDoorsBetween(gox, goy, goz, gtx, gty, gtz, instance, false)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } -- if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) -+ if (FenceData.getInstance().checkIfFenceBetween(gox, goy, goz, gtx, gty, gtz, instance)) - { -- return false; -+ return new GeoLocation(gox, goy, goz); - } - -- final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); -- // First point is guaranteed to be available. -- pointIter.next(); -- int prevX = pointIter.x(); -- int prevY = pointIter.y(); -- int prevZ = fromZ; -+ // get X delta, signum and direction flag -+ final int dx = Math.abs(gtx - gox); -+ final int sx = gox < gtx ? 1 : -1; -+ final byte dirX = sx > 0 ? GeoStructure.CELL_FLAG_E : GeoStructure.CELL_FLAG_W; - -- while (pointIter.next()) -+ // get Y delta, signum and direction flag -+ final int dy = Math.abs(gty - goy); -+ final int sy = goy < gty ? 1 : -1; -+ final byte dirY = sy > 0 ? GeoStructure.CELL_FLAG_S : GeoStructure.CELL_FLAG_N; -+ -+ // get direction flag for diagonal movement -+ final byte dirXY = getDirXY(dirX, dirY); -+ -+ // delta, determines axis to move on (+..X axis, -..Y axis) -+ int d = dx - dy; -+ -+ // NSWE direction of movement -+ byte direction; -+ -+ // load pointer coordinates -+ int gpx = gox; -+ int gpy = goy; -+ int gpz = goz; -+ -+ // load next pointer -+ int nx = gpx; -+ int ny = gpy; -+ -+ // loop -+ int count = 0; -+ while (count++ < Config.MAX_ITERATIONS) - { -- final int curX = pointIter.x(); -- final int curY = pointIter.y(); -- final int curZ = getNearestZ(curX, curY, prevZ); -+ direction = 0; - -- if (hasGeoPos(prevX, prevY)) -+ // calculate next point coordinates -+ final int e2 = 2 * d; -+ if ((e2 > -dy) && (e2 < dx)) - { -- final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); -- if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) -+ d -= dy; -+ d += dx; -+ nx += sx; -+ ny += sy; -+ direction |= dirXY; -+ } -+ else if (e2 > -dy) -+ { -+ d -= dy; -+ nx += sx; -+ direction |= dirX; -+ } -+ else if (e2 < dx) -+ { -+ d += dx; -+ ny += sy; -+ direction |= dirY; -+ } -+ -+ // obstacle found, return -+ if ((getNsweNearest(gpx, gpy, gpz) & direction) == 0) -+ { -+ return new GeoLocation(gpx, gpy, gpz); -+ } -+ -+ // update pointer coordinates -+ gpx = nx; -+ gpy = ny; -+ gpz = getHeightNearest(nx, ny, gpz); -+ -+ // target coordinates reached -+ if ((gpx == gtx) && (gpy == gty)) -+ { -+ if (gpz == gtz) - { -- return false; -+ // path found, Z coordinates are okay, return target point -+ return new GeoLocation(gtx, gty, gtz); - } -+ -+ // path found, Z coordinates are not okay, return last good point -+ return new GeoLocation(gpx, gpy, gpz); - } -+ } -+ -+ return new GeoLocation(gox, goy, goz); -+ } -+ -+ /** -+ * Returns diagonal NSWE flag format of combined two NSWE flags. -+ * @param dirX : X direction NSWE flag -+ * @param dirY : Y direction NSWE flag -+ * @return byte : NSWE flag of combined direction -+ */ -+ private static byte getDirXY(byte dirX, byte dirY) -+ { -+ // check axis directions -+ if (dirY == GeoStructure.CELL_FLAG_N) -+ { -+ if (dirX == GeoStructure.CELL_FLAG_W) -+ { -+ return GeoStructure.CELL_FLAG_NW; -+ } - -- prevX = curX; -- prevY = curY; -- prevZ = curZ; -+ return GeoStructure.CELL_FLAG_NE; - } - -- if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) -+ if (dirX == GeoStructure.CELL_FLAG_W) - { -- // Different floors. -- return false; -+ return GeoStructure.CELL_FLAG_SW; - } - -- return true; -+ return GeoStructure.CELL_FLAG_SE; - } - -+ /** -+ * Returns the list of location objects as a result of complete path calculation. -+ * @param ox : origin x -+ * @param oy : origin y -+ * @param oz : origin z -+ * @param tx : target x -+ * @param ty : target y -+ * @param tz : target z -+ * @param instance -+ * @return {@code List} : complete path from nodes -+ */ -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) -+ { -+ return null; -+ } -+ -+ /** -+ * Returns the instance of the {@link GeoEngine}. -+ * @return {@link GeoEngine} : The instance. -+ */ - public static GeoEngine getInstance() - { - return SingletonHolder.INSTANCE; -@@ -743,6 +934,6 @@ - - private static class SingletonHolder - { -- protected static final GeoEngine INSTANCE = new GeoEngine(); -+ protected static final GeoEngine INSTANCE = Config.PATHFINDING ? new GeoEnginePathfinding() : new GeoEngine(); - } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java (working copy) -@@ -20,88 +20,84 @@ - import java.util.LinkedList; - import java.util.List; - import java.util.ListIterator; --import java.util.logging.Level; --import java.util.logging.Logger; - - import org.l2jmobius.Config; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; --import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; --import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; --import org.l2jmobius.gameserver.model.World; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+import org.l2jmobius.gameserver.geoengine.pathfinding.Node; -+import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; -+import org.l2jmobius.gameserver.model.Location; - import org.l2jmobius.gameserver.model.instancezone.Instance; - - /** -- * @author -Nemesiss- -+ * @author Hasha - */ --public class GeoEnginePathfinding -+final class GeoEnginePathfinding extends GeoEngine - { -- private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); -+ // pre-allocated buffers -+ private final BufferHolder[] _buffers; - -- private BufferInfo[] _buffers; -- - protected GeoEnginePathfinding() - { -- try -+ super(); -+ -+ final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ _buffers = new BufferHolder[array.length]; -+ int count = 0; -+ for (int i = 0; i < array.length; i++) - { -- final String[] array = Config.PATHFIND_BUFFERS.split(";"); -+ final String buf = array[i]; -+ final String[] args = buf.split("x"); - -- _buffers = new BufferInfo[array.length]; -- -- String buf; -- String[] args; -- for (int i = 0; i < array.length; i++) -+ try - { -- buf = array[i]; -- args = buf.split("x"); -- if (args.length != 2) -- { -- throw new Exception("Invalid buffer definition: " + buf); -- } -- -- _buffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1])); -+ final int size = Integer.parseInt(args[1]); -+ count += size; -+ _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); - } -+ catch (Exception e) -+ { -+ LOGGER.warning("GeoEnginePathfinding: Can not load buffer setting: " + buf); -+ } - } -- catch (Exception e) -- { -- LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e); -- throw new Error("CellPathFinding: load aborted"); -- } -+ -+ LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers."); - } - -- public boolean pathNodesExist(short regionoffset) -+ @Override -+ public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) - { -- return false; -- } -- -- public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) -- { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -- if (!GeoEngine.getInstance().hasGeo(x, y)) -+ // get origin and check existing geo coords -+ final int gox = getGeoX(ox); -+ final int goy = getGeoY(oy); -+ if (!hasGeoPos(gox, goy)) - { - 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)) -+ -+ final short goz = getHeightNearest(gox, goy, oz); -+ -+ // get target and check existing geo coords -+ final int gtx = getGeoX(tx); -+ final int gty = getGeoY(ty); -+ if (!hasGeoPos(gtx, gty)) - { - 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)))); -+ -+ final short gtz = getHeightNearest(gtx, gty, tz); -+ -+ // Prepare buffer for pathfinding calculations -+ final NodeBuffer buffer = getBuffer(64 + (2 * Math.max(Math.abs(gox - gtx), Math.abs(goy - gty)))); - if (buffer == null) - { - return null; - } - -- List path = null; -+ // find path -+ List path = null; - try - { -- final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); -- -+ final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz); - if (result == null) - { - return null; -@@ -125,33 +121,45 @@ - return path; - } - -- int currentX, currentY, currentZ; -- ListIterator middlePoint; -+ // get path list iterator -+ final ListIterator point = path.listIterator(); - -- middlePoint = path.listIterator(); -- currentX = x; -- currentY = y; -- currentZ = z; -+ // get node A (origin) -+ int nodeAx = gox; -+ int nodeAy = goy; -+ short nodeAz = goz; - -- while (middlePoint.hasNext()) -+ // get node B -+ GeoLocation nodeB = (GeoLocation) point.next(); -+ -+ // iterate thought the path to optimize it -+ int count = 0; -+ while (point.hasNext() && (count++ < Config.MAX_ITERATIONS)) - { -- final AbstractNodeLoc locMiddle = middlePoint.next(); -- if (!middlePoint.hasNext()) -- { -- break; -- } -+ // get node C -+ final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex()); - -- final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); -- if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) -+ // check movement from node A to node C -+ final GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance); -+ if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY())) - { -- middlePoint.remove(); -+ // can move from node A to node C -+ -+ // remove node B -+ point.remove(); - } - else - { -- currentX = locMiddle.getX(); -- currentY = locMiddle.getY(); -- currentZ = locMiddle.getZ(); -+ // can not move from node A to node C -+ -+ // set node A (node B is part of path, update A coordinates) -+ nodeAx = nodeB.getGeoX(); -+ nodeAy = nodeB.getGeoY(); -+ nodeAz = (short) nodeB.getZ(); - } -+ -+ // set node B -+ nodeB = (GeoLocation) point.next(); - } - - return path; -@@ -158,144 +166,102 @@ - } - - /** -- * Convert geodata position to pathnode position -- * @param geo_pos -- * @return pathnode position -+ * Create list of node locations as result of calculated buffer node tree. -+ * @param node : the entry point -+ * @return List : list of node location - */ -- public short getNodePos(int geo_pos) -+ private static List constructPath(Node node) - { -- return (short) (geo_pos >> 3); // OK? -- } -- -- /** -- * Convert node position to pathnode block position -- * @param node_pos -- * @return pathnode block position (0...255) -- */ -- public short getNodeBlock(int node_pos) -- { -- return (short) (node_pos % 256); -- } -- -- public byte getRegionX(int node_pos) -- { -- return (byte) ((node_pos >> 8) + World.TILE_X_MIN); -- } -- -- public byte getRegionY(int node_pos) -- { -- return (byte) ((node_pos >> 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 node_x rx -- * @return -- */ -- public int calculateWorldX(short node_x) -- { -- return World.MAP_MIN_X + (node_x * 128) + 48; -- } -- -- /** -- * Convert pathnode y to World y position -- * @param node_y -- * @return -- */ -- public int calculateWorldY(short node_y) -- { -- return World.MAP_MIN_Y + (node_y * 128) + 48; -- } -- -- private List constructPath(AbstractNode nodeValue) -- { -- final LinkedList path = new LinkedList<>(); -- int previousDirectionX = Integer.MIN_VALUE; -- int previousDirectionY = Integer.MIN_VALUE; -- int directionX, directionY; -+ // create empty list -+ final LinkedList list = new LinkedList<>(); - -- AbstractNode node = nodeValue; -- while (node.getParent() != null) -+ // set direction X/Y -+ int dx = 0; -+ int dy = 0; -+ -+ // get target parent -+ Node target = node; -+ Node parent = target.getParent(); -+ -+ // while parent exists -+ int count = 0; -+ while ((parent != null) && (count++ < Config.MAX_ITERATIONS)) - { -- directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); -- directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); -+ // get parent <> target direction X/Y -+ final int nx = parent.getLoc().getGeoX() - target.getLoc().getGeoX(); -+ final int ny = parent.getLoc().getGeoY() - target.getLoc().getGeoY(); - -- // only add a new route point if moving direction changes -- if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) -+ // direction has changed? -+ if ((dx != nx) || (dy != ny)) - { -- previousDirectionX = directionX; -- previousDirectionY = directionY; -+ // add node to the beginning of the list -+ list.addFirst(target.getLoc()); - -- path.addFirst(node.getLoc()); -- node.setLoc(null); -+ // update direction X/Y -+ dx = nx; -+ dy = ny; - } - -- node = node.getParent(); -+ // move to next node, set target and get its parent -+ target = parent; -+ parent = target.getParent(); - } - -- return path; -+ // return list -+ return list; - } - -- private CellNodeBuffer alloc(int size) -+ /** -+ * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer. -+ * @param size : pre-calculated minimal required size -+ * @return NodeBuffer : buffer -+ */ -+ private final NodeBuffer getBuffer(int size) - { -- CellNodeBuffer current = null; -- for (BufferInfo i : _buffers) -+ NodeBuffer current = null; -+ for (BufferHolder holder : _buffers) - { -- if (i.mapSize >= size) -+ // Find proper size of buffer -+ if (holder._size < size) - { -- for (CellNodeBuffer buf : i.buffer) -+ continue; -+ } -+ -+ // Find unlocked NodeBuffer -+ for (NodeBuffer buffer : holder._buffer) -+ { -+ if (!buffer.isLocked()) - { -- if (buf.lock()) -- { -- current = buf; -- break; -- } -+ continue; - } -- if (current != null) -- { -- break; -- } - -- // not found, allocate temporary buffer -- current = new CellNodeBuffer(i.mapSize); -- current.lock(); -- if (i.buffer.size() < i.count) -- { -- i.buffer.add(current); -- break; -- } -+ return buffer; - } -+ -+ // NodeBuffer not found, allocate temporary buffer -+ current = new NodeBuffer(holder._size); -+ current.isLocked(); - } - - return current; - } - -- private static final class BufferInfo -+ /** -+ * NodeBuffer container with specified size and count of separate buffers. -+ */ -+ private static final class BufferHolder - { -- final int mapSize; -- final int count; -- ArrayList buffer; -+ final int _size; -+ List _buffer; - -- public BufferInfo(int size, int cnt) -+ public BufferHolder(int size, int count) - { -- mapSize = size; -- count = cnt; -- buffer = new ArrayList<>(count); -+ _size = size; -+ _buffer = new ArrayList<>(count); -+ for (int i = 0; i < count; i++) -+ { -+ _buffer.add(new NodeBuffer(size)); -+ } - } - } -- -- public static GeoEnginePathfinding getInstance() -- { -- return SingletonHolder.INSTANCE; -- } -- -- private static class SingletonHolder -- { -- protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); -- } --} -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java (working copy) -@@ -0,0 +1,197 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @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 height of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getHeightNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first above given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, above given coordinates. -+ */ -+ public abstract short getHeightAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell, which is first below given coordinates. -+ * @param geoX : Cell geodata X coordinate. -+ * @param geoY : Cell geodata Y coordinate. -+ * @param worldZ : Cell world Z coordinate. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is closest to given coordinates. -+ * @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 the NSWE flag byte of cell, which is closest to given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getNsweNearestOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first above given coordinates. -+ * @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 getNsweAbove(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the NSWE flag byte of cell, which is first below given coordinates. -+ * @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 getNsweBelow(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is closes layer to given coordinates. -+ * @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. -+ * @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 above given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns index to data of the cell, which is first layer below given coordinates. -+ * @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 index to data of the cell, which is first layer below given coordinates.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @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 getIndexBelowOriginal(int geoX, int geoY, int worldZ); -+ -+ /** -+ * Returns the height of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeight(int index); -+ -+ /** -+ * Returns the height of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract short getHeightOriginal(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNswe(int index); -+ -+ /** -+ * Returns the NSWE flag byte of cell given by cell index.
-+ * Geodata without {@link IGeoObject} are taken in consideration. -+ * @param index : Index of the cell. -+ * @return short : Cell geodata Z coordinate, below given coordinates. -+ */ -+ public abstract byte getNsweOriginal(int index); -+ -+ /** -+ * Sets the NSWE flag byte of cell given by cell index. -+ * @param index : Index of the cell. -+ * @param nswe : New NSWE flag byte. -+ */ -+ public abstract void setNswe(int index, byte nswe); -+ -+ /** -+ * Saves the block in L2D format to {@link BufferedOutputStream}. Used only for L2D geodata conversion. -+ * @param stream : The stream. -+ * @throws IOException : Can't save the block to steam. -+ */ -+ public abstract void saveBlock(BufferedOutputStream stream) throws IOException; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java (working copy) -@@ -0,0 +1,252 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+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. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockComplex(ByteBuffer bb, GeoFormat format) -+ { -+ // initialize buffer -+ _buffer = new byte[GeoStructure.BLOCK_CELLS * 3]; -+ -+ // load data -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // 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); -+ } -+ else -+ { -+ // get nswe -+ final byte nswe = bb.get(); -+ _buffer[i * 3] = nswe; -+ -+ // get height -+ final short height = bb.getShort(); -+ _buffer[(i * 3) + 1] = (byte) (height & 0x00FF); -+ _buffer[(i * 3) + 2] = (byte) (height >> 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height > worldZ ? height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 short height = (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ -+ // check and return height -+ return height < worldZ ? height : Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 ? _buffer[index] : 0; -+ } -+ -+ @Override -+ public 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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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 int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_COMPLEX_L2D); -+ -+ // write block data -+ stream.write(_buffer, 0, GeoStructure.BLOCK_CELLS * 3); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java (working copy) -@@ -0,0 +1,176 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockFlat extends ABlock -+{ -+ protected final short _height; -+ protected byte _nswe; -+ -+ /** -+ * Creates FlatBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockFlat(ByteBuffer bb, GeoFormat format) -+ { -+ _height = bb.getShort(); -+ _nswe = format != GeoFormat.L2D ? 0x0F : (byte) (0xFF); -+ if (format == GeoFormat.L2OFF) -+ { -+ bb.getShort(); -+ } -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return true; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height > worldZ ? _height : Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ // check and return height -+ return _height < worldZ ? _height : Short.MAX_VALUE; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height > worldZ ? _nswe : 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return nswe -+ return _height < worldZ ? _nswe : 0; -+ } -+ -+ @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 getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ // check height and return index -+ return _height < worldZ ? 0 : -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return _height; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ _nswe = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_FLAT_L2D); -+ -+ // write height -+ stream.write((byte) (_height & 0x00FF)); -+ stream.write((byte) (_height >> 8)); -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java (working copy) -@@ -0,0 +1,465 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+import java.io.IOException; -+import java.nio.ByteBuffer; -+import java.nio.ByteOrder; -+import java.util.Arrays; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockMultilayer extends ABlock -+{ -+ private static final int MAX_LAYERS = Byte.MAX_VALUE; -+ -+ private static ByteBuffer _temp; -+ -+ /** -+ * Initializes the temporarily buffer. -+ */ -+ public static void initialize() -+ { -+ // initialize temporarily buffer and sorting mechanism -+ _temp = ByteBuffer.allocate(GeoStructure.BLOCK_CELLS * MAX_LAYERS * 3); -+ _temp.order(ByteOrder.LITTLE_ENDIAN); -+ } -+ -+ /** -+ * Releases temporarily buffer. -+ */ -+ public static void release() -+ { -+ _temp = null; -+ } -+ -+ protected byte[] _buffer; -+ -+ /** -+ * Implicit constructor for children class. -+ */ -+ protected BlockMultilayer() -+ { -+ _buffer = null; -+ } -+ -+ /** -+ * Creates MultilayerBlock. -+ * @param bb : Input byte buffer. -+ * @param format : GeoFormat specifying format of loaded data. -+ */ -+ public BlockMultilayer(ByteBuffer bb, GeoFormat format) -+ { -+ // 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 = format != GeoFormat.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++) -+ { -+ if (format != GeoFormat.L2D) -+ { -+ // get data -+ final short data = bb.getShort(); -+ -+ // add nswe and height -+ _temp.put((byte) (data & 0x000F)); -+ _temp.putShort((short) ((short) (data & 0xFFF0) >> 1)); -+ } -+ else -+ { -+ // add nswe -+ _temp.put(bb.get()); -+ -+ // add height -+ _temp.putShort(bb.getShort()); -+ } -+ } -+ } -+ -+ // 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 short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getHeightNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeightAbove(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 height -+ if (height > worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, return minimum value -+ return Short.MIN_VALUE; -+ } -+ -+ @Override -+ public short getHeightBelow(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 height -+ if (height < worldZ) -+ { -+ return (short) height; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, return maximum value -+ return Short.MAX_VALUE; -+ } -+ -+ @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 byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getNsweNearest(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public byte getNsweAbove(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 nswe -+ if (height > worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index -= 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @Override -+ public byte getNsweBelow(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 nswe -+ if (height < worldZ) -+ { -+ return _buffer[index]; -+ } -+ -+ // move index to next layer -+ index += 3; -+ } -+ -+ // none layer found, block movement -+ return 0; -+ } -+ -+ @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 first layer data (first from bottom) -+ byte layers = _buffer[index++]; -+ -+ // loop though all cell layers, find closest layer -+ 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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexAbove(geoX, geoY, worldZ); -+ } -+ -+ @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; -+ } -+ -+ // none layer found -+ return -1; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return getIndexBelow(geoX, geoY, worldZ); -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ // get height -+ return (short) ((_buffer[index + 1] & 0x00FF) | (_buffer[index + 2] << 8)); -+ } -+ -+ @Override -+ public short getHeightOriginal(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]; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ // get nswe -+ return _buffer[index]; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ // set nswe -+ _buffer[index] = nswe; -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) throws IOException -+ { -+ // write block type -+ stream.write(GeoStructure.TYPE_MULTILAYER_L2D); -+ -+ // for each cell -+ int index = 0; -+ for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++) -+ { -+ // write layers count -+ final byte layers = _buffer[index++]; -+ stream.write(layers); -+ -+ // write cell data -+ stream.write(_buffer, index, layers * 3); -+ -+ // move index to next cell -+ index += layers * 3; -+ } -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java (working copy) -@@ -0,0 +1,150 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import java.io.BufferedOutputStream; -+ -+/** -+ * @author Hasha -+ */ -+public class BlockNull extends ABlock -+{ -+ private final byte _nswe; -+ -+ public BlockNull() -+ { -+ _nswe = (byte) 0xFF; -+ } -+ -+ @Override -+ public boolean hasGeoPos() -+ { -+ return false; -+ } -+ -+ @Override -+ public short getHeightNearest(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightAbove(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public short getHeightBelow(int geoX, int geoY, int worldZ) -+ { -+ return (short) worldZ; -+ } -+ -+ @Override -+ public byte getNsweNearest(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweAbove(int geoX, int geoY, int worldZ) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweBelow(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) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexAboveOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelow(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public int getIndexBelowOriginal(int geoX, int geoY, int worldZ) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeight(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public short getHeightOriginal(int index) -+ { -+ return 0; -+ } -+ -+ @Override -+ public byte getNswe(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public byte getNsweOriginal(int index) -+ { -+ return _nswe; -+ } -+ -+ @Override -+ public void setNswe(int index, byte nswe) -+ { -+ } -+ -+ @Override -+ public void saveBlock(BufferedOutputStream stream) -+ { -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java (nonexistent) -@@ -1,48 +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 . -- */ --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() -- { -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java (nonexistent) -@@ -1,77 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java (nonexistent) -@@ -1,56 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoFormat.java (working copy) -@@ -0,0 +1,39 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public enum GeoFormat -+{ -+ L2J("%d_%d.l2j"), -+ L2OFF("%d_%d_conv.dat"), -+ L2D("%d_%d.l2d"); -+ -+ private final String _filename; -+ -+ private GeoFormat(String filename) -+ { -+ _filename = filename; -+ } -+ -+ public String getFilename() -+ { -+ return _filename; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoLocation.java (working copy) -@@ -0,0 +1,67 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.geoengine.GeoEngine; -+import org.l2jmobius.gameserver.model.Location; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoLocation extends Location -+{ -+ private byte _nswe; -+ -+ public GeoLocation(int x, int y, int z) -+ { -+ super(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public void set(int x, int y, short z) -+ { -+ super.setXYZ(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z)); -+ _nswe = GeoEngine.getInstance().getNsweNearest(x, y, z); -+ } -+ -+ public int getGeoX() -+ { -+ return _x; -+ } -+ -+ public int getGeoY() -+ { -+ return _y; -+ } -+ -+ @Override -+ public int getX() -+ { -+ return GeoEngine.getWorldX(_x); -+ } -+ -+ @Override -+ public int getY() -+ { -+ return GeoEngine.getWorldY(_y); -+ } -+ -+ public byte getNSWE() -+ { -+ return _nswe; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java (working copy) -@@ -0,0 +1,70 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoStructure -+{ -+ // cells -+ public static final byte CELL_FLAG_E = 1 << 0; -+ public static final byte CELL_FLAG_W = 1 << 1; -+ public static final byte CELL_FLAG_S = 1 << 2; -+ public static final byte CELL_FLAG_N = 1 << 3; -+ public static final byte CELL_FLAG_SE = 1 << 4; -+ public static final byte CELL_FLAG_SW = 1 << 5; -+ public static final byte CELL_FLAG_NE = 1 << 6; -+ public static final byte CELL_FLAG_NW = (byte) (1 << 7); -+ -+ public static final int CELL_HEIGHT = 8; -+ public static final int CELL_IGNORE_HEIGHT = CELL_HEIGHT * 6; -+ -+ // blocks -+ public static final byte TYPE_FLAT_L2J_L2OFF = 0; -+ public static final byte TYPE_FLAT_L2D = (byte) 0xD0; -+ public static final byte TYPE_COMPLEX_L2J = 1; -+ public static final byte TYPE_COMPLEX_L2OFF = 0x40; -+ public static final byte TYPE_COMPLEX_L2D = (byte) 0xD1; -+ 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) -+ public static final byte TYPE_MULTILAYER_L2D = (byte) 0xD2; -+ -+ 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; -+ -+ // regions -+ 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; -+ -+ // global geodata -+ private static final int GEO_REGIONS_X = ((World.TILE_X_MAX - World.TILE_X_MIN) + 1); -+ private 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; -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java (nonexistent) -@@ -1,42 +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 . -- */ --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); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IGeoObject.java (working copy) -@@ -0,0 +1,53 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.geodata; -+ -+/** -+ * @author Hasha -+ */ -+public interface IGeoObject -+{ -+ /** -+ * Returns geodata X coordinate of the {@link IGeoObject}. -+ * @return int : Geodata X coordinate. -+ */ -+ int getGeoX(); -+ -+ /** -+ * Returns geodata Y coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Y coordinate. -+ */ -+ int getGeoY(); -+ -+ /** -+ * Returns geodata Z coordinate of the {@link IGeoObject}. -+ * @return int : Geodata Z coordinate. -+ */ -+ int getGeoZ(); -+ -+ /** -+ * Returns height of the {@link IGeoObject}. -+ * @return int : Height. -+ */ -+ int getHeight(); -+ -+ /** -+ * Returns {@link IGeoObject} data. -+ * @return byte[][] : {@link IGeoObject} data. -+ */ -+ byte[][] getObjectGeoData(); -+} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java (nonexistent) -@@ -1,47 +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 . -- */ --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(); --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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); -- -- // 1 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java (nonexistent) -@@ -1,55 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --/** -- * @author HorridoJoho -- */ --public final class NullRegion implements IRegion --{ -- public static final NullRegion INSTANCE = new NullRegion(); -- -- @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 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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/geodata/Region.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/geodata/Region.java (nonexistent) -@@ -1,92 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.geodata; -- --import java.nio.ByteBuffer; -- --/** -- * @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; -- } --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java (nonexistent) -@@ -1,87 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public abstract class AbstractNode --{ -- private T _loc; -- private AbstractNode _parent; -- -- public AbstractNode(T loc) -- { -- _loc = loc; -- } -- -- public void setParent(AbstractNode p) -- { -- _parent = p; -- } -- -- public AbstractNode getParent() -- { -- return _parent; -- } -- -- public T getLoc() -- { -- return _loc; -- } -- -- public void setLoc(T l) -- { -- _loc = l; -- } -- -- @Override -- public int hashCode() -- { -- final int prime = 31; -- int result = 1; -- result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); -- return result; -- } -- -- @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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java (nonexistent) -@@ -1,33 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --/** -- * @author -Nemesiss- -- */ --public abstract class AbstractNodeLoc --{ -- public abstract int getX(); -- -- public abstract int getY(); -- -- public abstract int getZ(); -- -- public abstract int getNodeX(); -- -- public abstract int getNodeY(); --} -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java (nonexistent) -@@ -1,67 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --public class CellNode extends AbstractNode --{ -- 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java (nonexistent) -@@ -1,348 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --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 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) -- { -- _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(); -- } -- -- public final List debugPath() -- { -- final List 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 void getNeighbors() -- { -- if (!_current.getLoc().canGoAll()) -- { -- 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); -- } -- -- // SouthEast -- if ((nodeE != null) && (nodeS != null)) -- { -- if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) -- { -- addNode(x + 1, y + 1, z, true); -- } -- } -- -- // SouthWest -- if ((nodeS != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) -- { -- addNode(x - 1, y + 1, z, true); -- } -- } -- -- // NorthEast -- if ((nodeN != null) && (nodeE != null)) -- { -- if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) -- { -- addNode(x + 1, y - 1, z, true); -- } -- } -- -- // NorthWest -- if ((nodeN != null) && (nodeW != null)) -- { -- if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) -- { -- addNode(x - 1, y - 1, z, true); -- } -- } -- } -- -- private 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(); -- // reinit node if needed -- if (result.getLoc() != null) -- { -- result.getLoc().set(x, y, z); -- } -- else -- { -- result.setLoc(new NodeLoc(x, y, z)); -- } -- } -- -- return result; -- } -- -- private 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 boolean isHighWeight(int x, int y, int z) -- { -- final CellNode result = getNode(x, y, z); -- if (result == null) -- { -- return true; -- } -- -- if (!result.getLoc().canGoAll()) -- { -- return true; -- } -- if (Math.abs(result.getLoc().getZ() - z) > 16) -- { -- return true; -- } -- -- return false; -- } -- -- private 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; -- } --} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java (working copy) -@@ -0,0 +1,87 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import org.l2jmobius.gameserver.geoengine.geodata.GeoLocation; -+ -+/** -+ * @author Hasha -+ */ -+public class Node -+{ -+ // node coords and nswe flag -+ private GeoLocation _loc; -+ -+ // node parent (for reverse path construction) -+ private Node _parent; -+ // node child (for moving over nodes during iteration) -+ private Node _child; -+ -+ // node G cost (movement cost = parent movement cost + current movement cost) -+ private double _cost = -1000; -+ -+ public void setLoc(int x, int y, int z) -+ { -+ _loc = new GeoLocation(x, y, z); -+ } -+ -+ public GeoLocation getLoc() -+ { -+ return _loc; -+ } -+ -+ public void setParent(Node parent) -+ { -+ _parent = parent; -+ } -+ -+ public Node getParent() -+ { -+ return _parent; -+ } -+ -+ public void setChild(Node child) -+ { -+ _child = child; -+ } -+ -+ public Node getChild() -+ { -+ return _child; -+ } -+ -+ public void setCost(double cost) -+ { -+ _cost = cost; -+ } -+ -+ public double getCost() -+ { -+ return _cost; -+ } -+ -+ public void free() -+ { -+ // reset node location -+ _loc = null; -+ -+ // reset node parent, child and cost -+ _parent = null; -+ _child = null; -+ _cost = -1000; -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (nonexistent) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java (working copy) -@@ -0,0 +1,306 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.gameserver.geoengine.pathfinding; -+ -+import java.util.concurrent.locks.ReentrantLock; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+ -+/** -+ * @author DS, Hasha; Credits to Diamond -+ */ -+public class NodeBuffer -+{ -+ private final ReentrantLock _lock = new ReentrantLock(); -+ private final int _size; -+ private final Node[][] _buffer; -+ -+ // center coordinates -+ private int _cx = 0; -+ private int _cy = 0; -+ -+ // target coordinates -+ private int _gtx = 0; -+ private int _gty = 0; -+ private short _gtz = 0; -+ -+ private Node _current = null; -+ -+ /** -+ * Constructor of NodeBuffer. -+ * @param size : one dimension size of buffer -+ */ -+ public NodeBuffer(int size) -+ { -+ // set size -+ _size = size; -+ -+ // initialize buffer -+ _buffer = new Node[size][size]; -+ for (int x = 0; x < size; x++) -+ { -+ for (int y = 0; y < size; y++) -+ { -+ _buffer[x][y] = 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 Node : first node of path -+ */ -+ public Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz) -+ { -+ // set coordinates (middle of the line (gox,goy) - (gtx,gty), will be in the center of the buffer) -+ _cx = gox + ((gtx - gox - _size) / 2); -+ _cy = goy + ((gty - goy - _size) / 2); -+ -+ _gtx = gtx; -+ _gty = gty; -+ _gtz = gtz; -+ -+ _current = getNode(gox, goy, goz); -+ _current.setCost(getCostH(gox, goy, goz)); -+ -+ int count = 0; -+ do -+ { -+ // reached target? -+ if ((_current.getLoc().getGeoX() == _gtx) && (_current.getLoc().getGeoY() == _gty) && (Math.abs(_current.getLoc().getZ() - _gtz) < 8)) -+ { -+ return _current; -+ } -+ -+ // expand current node -+ expand(); -+ -+ // move pointer -+ _current = _current.getChild(); -+ } -+ while ((_current != null) && (count++ < Config.MAX_ITERATIONS)); -+ -+ return null; -+ } -+ -+ public boolean isLocked() -+ { -+ return _lock.tryLock(); -+ } -+ -+ public void free() -+ { -+ _current = null; -+ -+ for (Node[] nodes : _buffer) -+ { -+ for (Node node : nodes) -+ { -+ if (node.getLoc() != null) -+ { -+ node.free(); -+ } -+ } -+ } -+ -+ _lock.unlock(); -+ } -+ -+ /** -+ * Check _current Node and add its neighbors to the buffer. -+ */ -+ private final void expand() -+ { -+ // can't move anywhere, don't expand -+ final byte nswe = _current.getLoc().getNSWE(); -+ if (nswe == 0) -+ { -+ return; -+ } -+ -+ // get geo coords of the node to be expanded -+ final int x = _current.getLoc().getGeoX(); -+ final int y = _current.getLoc().getGeoY(); -+ final short z = (short) _current.getLoc().getZ(); -+ -+ // can move north, expand -+ if ((nswe & GeoStructure.CELL_FLAG_N) != 0) -+ { -+ addNode(x, y - 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move south, expand -+ if ((nswe & GeoStructure.CELL_FLAG_S) != 0) -+ { -+ addNode(x, y + 1, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_W) != 0) -+ { -+ addNode(x - 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_E) != 0) -+ { -+ addNode(x + 1, y, z, Config.BASE_WEIGHT); -+ } -+ -+ // can move north-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NW) != 0) -+ { -+ addNode(x - 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move north-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_NE) != 0) -+ { -+ addNode(x + 1, y - 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-west, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SW) != 0) -+ { -+ addNode(x - 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ -+ // can move south-east, expand -+ if ((nswe & GeoStructure.CELL_FLAG_SE) != 0) -+ { -+ addNode(x + 1, y + 1, z, Config.DIAGONAL_WEIGHT); -+ } -+ } -+ -+ /** -+ * Returns node, if it exists in buffer. -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param z : node Z coord -+ * @return Node : node, if exits in buffer -+ */ -+ private final Node getNode(int x, int y, short z) -+ { -+ // check node X out of coordinates -+ final int ix = x - _cx; -+ if ((ix < 0) || (ix >= _size)) -+ { -+ return null; -+ } -+ -+ // check node Y out of coordinates -+ final int iy = y - _cy; -+ if ((iy < 0) || (iy >= _size)) -+ { -+ return null; -+ } -+ -+ // get node -+ final Node result = _buffer[ix][iy]; -+ -+ // check and update -+ if (result.getLoc() == null) -+ { -+ result.setLoc(x, y, z); -+ } -+ -+ // return node -+ return result; -+ } -+ -+ /** -+ * Add node given by coordinates to the buffer. -+ * @param x : geo X coord -+ * @param y : geo Y coord -+ * @param z : geo Z coord -+ * @param weight : weight of movement to new node -+ */ -+ private final void addNode(int x, int y, short z, int weight) -+ { -+ // get node to be expanded -+ final Node node = getNode(x, y, z); -+ if (node == null) -+ { -+ return; -+ } -+ -+ // Z distance between nearby cells is higher than cell size -+ if (node.getLoc().getZ() > (z + (2 * GeoStructure.CELL_HEIGHT))) -+ { -+ return; -+ } -+ -+ // node was already expanded, return -+ if (node.getCost() >= 0) -+ { -+ return; -+ } -+ -+ node.setParent(_current); -+ if (node.getLoc().getNSWE() != (byte) 0xFF) -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + (weight * Config.OBSTACLE_MULTIPLIER)); -+ } -+ else -+ { -+ node.setCost(getCostH(x, y, node.getLoc().getZ()) + weight); -+ } -+ -+ Node current = _current; -+ int count = 0; -+ while ((current.getChild() != null) && (count < (Config.MAX_ITERATIONS * 4))) -+ { -+ count++; -+ if (current.getChild().getCost() > node.getCost()) -+ { -+ node.setChild(current.getChild()); -+ break; -+ } -+ current = current.getChild(); -+ } -+ -+ if (count >= (Config.MAX_ITERATIONS * 4)) -+ { -+ System.err.println("Pathfinding: too long loop detected, cost:" + node.getCost()); -+ } -+ -+ current.setChild(node); -+ } -+ -+ /** -+ * @param x : node X coord -+ * @param y : node Y coord -+ * @param i : node Z coord -+ * @return double : node cost -+ */ -+ private final double getCostH(int x, int y, int i) -+ { -+ final int dX = x - _gtx; -+ final int dY = y - _gty; -+ final int dZ = (i - _gtz) / GeoStructure.CELL_HEIGHT; -+ -+ // return (Math.abs(dX) + Math.abs(dY) + Math.abs(dZ)) * Config.HEURISTIC_WEIGHT; // Manhattan distance -+ return Math.sqrt((dX * dX) + (dY * dY) + (dZ * dZ)) * Config.HEURISTIC_WEIGHT; // Direct distance -+ } -+} -\ No newline at end of file -Index: java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java -=================================================================== ---- java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (revision 8409) -+++ java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java (nonexistent) -@@ -1,183 +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 . -- */ --package org.l2jmobius.gameserver.geoengine.pathfinding; -- --import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -- --/** -- * @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 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 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; -- // return super.hashCode(); -- } -- -- @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; -- if (_x != other._x) -- { -- return false; -- } -- if (_y != other._y) -- { -- return false; -- } -- if (_goNorth != other._goNorth) -- { -- return false; -- } -- if (_goEast != other._goEast) -- { -- return false; -- } -- if (_goSouth != other._goSouth) -- { -- return false; -- } -- if (_goWest != other._goWest) -- { -- return false; -- } -- if (_geoHeight != other._geoHeight) -- { -- return false; -- } -- return true; -- } --} -Index: java/org/l2jmobius/gameserver/model/actor/Creature.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/Creature.java (revision 8428) -+++ java/org/l2jmobius/gameserver/model/actor/Creature.java (working copy) -@@ -65,8 +65,6 @@ - import org.l2jmobius.gameserver.enums.TeleportWhereType; - import org.l2jmobius.gameserver.enums.UserInfoType; - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; --import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; - import org.l2jmobius.gameserver.instancemanager.IdManager; - import org.l2jmobius.gameserver.instancemanager.MapRegionManager; - import org.l2jmobius.gameserver.instancemanager.QuestManager; -@@ -2566,7 +2564,7 @@ - - public boolean disregardingGeodata; - public int onGeodataPathIndex; -- public List geoPath; -+ public List geoPath; - public int geoPathAccurateTx; - public int geoPathAccurateTy; - public int geoPathGtx; -@@ -3443,7 +3441,7 @@ - if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) - { - // Path calculation -- overrides previous movement check -- m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); -+ m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); - if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found - { - if (isPlayer() && !_isFlying && !isInWater) -Index: java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java -=================================================================== ---- java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (revision 8424) -+++ java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java (working copy) -@@ -12746,7 +12746,7 @@ - { - if (Config.CORRECT_PLAYER_Z) - { -- final int nearestZ = GeoEngine.getInstance().getHigherHeight(getX(), getY(), getZ()); -+ final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ()); - if (getZ() < nearestZ) - { - teleToLocation(new Location(getX(), getY(), nearestZ)); -Index: java/org/l2jmobius/gameserver/util/GeoUtils.java -=================================================================== ---- java/org/l2jmobius/gameserver/util/GeoUtils.java (revision 8409) -+++ java/org/l2jmobius/gameserver/util/GeoUtils.java (working copy) -@@ -19,7 +19,7 @@ - import java.awt.Color; - - import org.l2jmobius.gameserver.geoengine.GeoEngine; --import org.l2jmobius.gameserver.geoengine.geodata.Cell; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; - import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; - import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; - -@@ -26,25 +26,25 @@ - /** - * @author HorridoJoho - */ --public final class GeoUtils -+public class GeoUtils - { - public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); - - final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); - - while (iter.next()) - { -- final int wx = GeoEngine.getInstance().getWorldX(iter.x()); -- final int wy = GeoEngine.getInstance().getWorldY(iter.y()); -+ final int wx = GeoEngine.getWorldX(iter.x()); -+ final int wy = GeoEngine.getWorldY(iter.y()); - - prim.addPoint(Color.RED, wx, wy, z); - } -@@ -53,21 +53,21 @@ - - public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) - { -- final int gx = GeoEngine.getInstance().getGeoX(x); -- final int gy = GeoEngine.getInstance().getGeoY(y); -+ final int gx = GeoEngine.getGeoX(x); -+ final int gy = GeoEngine.getGeoY(y); - -- final int tgx = GeoEngine.getInstance().getGeoX(tx); -- final int tgy = GeoEngine.getInstance().getGeoY(ty); -+ final int tgx = GeoEngine.getGeoX(tx); -+ final int tgy = GeoEngine.getGeoY(ty); - - final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); -- prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); -+ prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); -- int wy = GeoEngine.getInstance().getWorldY(prevY); -+ int wx = GeoEngine.getWorldX(prevX); -+ int wy = GeoEngine.getWorldY(prevY); - int wz = iter.z(); - prim.addPoint(Color.RED, wx, wy, wz); - -@@ -78,8 +78,8 @@ - - if ((curX != prevX) || (curY != prevY)) - { -- wx = GeoEngine.getInstance().getWorldX(curX); -- wy = GeoEngine.getInstance().getWorldY(curY); -+ wx = GeoEngine.getWorldX(curX); -+ wy = GeoEngine.getWorldY(curY); - wz = iter.z(); - - prim.addPoint(Color.RED, wx, wy, wz); -@@ -93,7 +93,7 @@ - - private static Color getDirectionColor(int x, int y, int z, int nswe) - { -- if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe)) -+ if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe) - { - return Color.GREEN; - } -@@ -109,9 +109,8 @@ - int iPacket = 0; - - ExServerPrimitive exsp = null; -- final GeoEngine ge = GeoEngine.getInstance(); -- final int playerGx = ge.getGeoX(player.getX()); -- final int playerGy = ge.getGeoY(player.getY()); -+ final int playerGx = GeoEngine.getGeoX(player.getX()); -+ final int playerGy = GeoEngine.getGeoY(player.getY()); - for (int dx = -geoRadius; dx <= geoRadius; ++dx) - { - for (int dy = -geoRadius; dy <= geoRadius; ++dy) -@@ -135,12 +134,12 @@ - final int gx = playerGx + dx; - final int gy = playerGy + dy; - -- final int x = ge.getWorldX(gx); -- final int y = ge.getWorldY(gy); -- final int z = ge.getNearestZ(gx, gy, player.getZ()); -+ final int x = GeoEngine.getWorldX(gx); -+ final int y = GeoEngine.getWorldY(gy); -+ final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); - - // north arrow -- Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); -+ Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); - 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); -@@ -147,7 +146,7 @@ - exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z); - - // east arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); - 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); -@@ -154,13 +153,13 @@ - exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z); - - // south arrow -- col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); - 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, Cell.NSWE_WEST); -+ col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); - 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); -@@ -188,15 +187,15 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_EAST; -+ return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_EAST; -+ return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST; - } - else - { -- return Cell.NSWE_EAST; -+ return GeoStructure.CELL_FLAG_E; // Direction.EAST; - } - } - else if (x < lastX) // west -@@ -203,26 +202,27 @@ - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH_WEST; -+ return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH_WEST; -+ return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST; - } - else - { -- return Cell.NSWE_WEST; -+ return GeoStructure.CELL_FLAG_W; // Direction.WEST; - } - } -- else // unchanged x -+ else -+ // unchanged x - { - if (y > lastY) - { -- return Cell.NSWE_SOUTH; -+ return GeoStructure.CELL_FLAG_S; // Direction.SOUTH; - } - else if (y < lastY) - { -- return Cell.NSWE_NORTH; -+ return GeoStructure.CELL_FLAG_N; // Direction.NORTH; - } - else - { -Index: java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java -=================================================================== ---- java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (nonexistent) -+++ java/org/l2jmobius/tools/geodataconverter/GeoDataConverter.java (working copy) -@@ -0,0 +1,386 @@ -+/* -+ * 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 . -+ */ -+package org.l2jmobius.tools.geodataconverter; -+ -+import java.io.BufferedOutputStream; -+import java.io.File; -+import java.io.FileOutputStream; -+import java.io.RandomAccessFile; -+import java.nio.ByteOrder; -+import java.nio.MappedByteBuffer; -+import java.nio.channels.FileChannel; -+import java.util.Scanner; -+ -+import org.l2jmobius.Config; -+import org.l2jmobius.commons.util.PropertiesParser; -+import org.l2jmobius.gameserver.geoengine.geodata.ABlock; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; -+import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoFormat; -+import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; -+import org.l2jmobius.gameserver.model.World; -+ -+/** -+ * @author Hasha -+ */ -+public class GeoDataConverter -+{ -+ private static GeoFormat _format; -+ private static ABlock[][] _blocks; -+ -+ public static void main(String[] args) -+ { -+ // initialize config -+ loadGeoengineConfigs(); -+ -+ // get geodata format -+ String type = ""; -+ try (Scanner scn = new Scanner(System.in)) -+ { -+ while (!(type.equalsIgnoreCase("J") || type.equalsIgnoreCase("O") || type.equalsIgnoreCase("E"))) -+ { -+ System.out.println("GeoDataConverter: Select source geodata type:"); -+ System.out.println(" J: L2J (e.g. 23_22.l2j)"); -+ System.out.println(" O: L2OFF (e.g. 23_22_conv.dat)"); -+ System.out.println(" E: Exit"); -+ System.out.print("Choice: "); -+ type = scn.next(); -+ } -+ } -+ if (type.equalsIgnoreCase("E")) -+ { -+ System.exit(0); -+ } -+ -+ _format = type.equalsIgnoreCase("J") ? GeoFormat.L2J : GeoFormat.L2OFF; -+ -+ // start conversion -+ System.out.println("GeoDataConverter: Converting all " + _format + " files."); -+ -+ // initialize geodata container -+ _blocks = new ABlock[GeoStructure.REGION_BLOCKS_X][GeoStructure.REGION_BLOCKS_Y]; -+ -+ // initialize multilayer temporarily buffer -+ BlockMultilayer.initialize(); -+ -+ // load geo files -+ int converted = 0; -+ for (int rx = World.TILE_X_MIN; rx <= World.TILE_X_MAX; rx++) -+ { -+ for (int ry = World.TILE_Y_MIN; ry <= World.TILE_Y_MAX; ry++) -+ { -+ final String input = String.format(_format.getFilename(), rx, ry); -+ final String filepath = Config.GEODATA_PATH; -+ final File f = new File(filepath + input); -+ if (f.exists() && !f.isDirectory()) -+ { -+ // load geodata -+ if (!loadGeoBlocks(input)) -+ { -+ System.out.println("GeoDataConverter: Unable to load " + input + " region file."); -+ continue; -+ } -+ -+ // recalculate nswe -+ if (!recalculateNswe()) -+ { -+ System.out.println("GeoDataConverter: Unable to convert " + input + " region file."); -+ continue; -+ } -+ -+ // save geodata -+ final String output = String.format(GeoFormat.L2D.getFilename(), rx, ry); -+ if (!saveGeoBlocks(output)) -+ { -+ System.out.println("GeoDataConverter: Unable to save " + output + " region file."); -+ continue; -+ } -+ -+ converted++; -+ System.out.println("GeoDataConverter: Created " + output + " region file."); -+ } -+ } -+ } -+ System.out.println("GeoDataConverter: Converted " + converted + " " + _format + " to L2D region file(s)."); -+ -+ // release multilayer block temporarily buffer -+ BlockMultilayer.release(); -+ } -+ -+ /** -+ * Loads geo blocks from buffer of the region file. -+ * @param filename : The name of the to load. -+ * @return boolean : True when successful. -+ */ -+ private static boolean loadGeoBlocks(String filename) -+ { -+ // region file is load-able, try to load it -+ try (RandomAccessFile raf = new RandomAccessFile(Config.GEODATA_PATH + filename, "r"); -+ FileChannel fc = raf.getChannel()) -+ { -+ final MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); -+ buffer.order(ByteOrder.LITTLE_ENDIAN); -+ -+ // load 18B header for L2off geodata (1st and 2nd byte...region X and Y) -+ if (_format == GeoFormat.L2OFF) -+ { -+ for (int i = 0; i < 18; i++) -+ { -+ buffer.get(); -+ } -+ } -+ -+ // loop over region blocks -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ if (_format == GeoFormat.L2J) -+ { -+ // get block type -+ final byte type = buffer.get(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2J: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_MULTILAYER_L2J: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ default: -+ { -+ throw new IllegalArgumentException("Unknown block type: " + type); -+ } -+ } -+ } -+ else -+ { -+ // get block type -+ final short type = buffer.getShort(); -+ -+ // load block according to block type -+ switch (type) -+ { -+ case GeoStructure.TYPE_FLAT_L2J_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockFlat(buffer, _format); -+ break; -+ } -+ case GeoStructure.TYPE_COMPLEX_L2OFF: -+ { -+ _blocks[ix][iy] = new BlockComplex(buffer, _format); -+ break; -+ } -+ default: -+ { -+ _blocks[ix][iy] = new BlockMultilayer(buffer, _format); -+ break; -+ } -+ } -+ } -+ } -+ } -+ -+ if (buffer.remaining() > 0) -+ { -+ System.out.println("GeoDataConverter: Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); -+ return false; -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ System.out.println("GeoDataConverter: Error while loading " + filename + " region file."); -+ return false; -+ } -+ } -+ -+ /** -+ * Recalculate diagonal flags for the region file. -+ * @return boolean : True when successful. -+ */ -+ private static boolean recalculateNswe() -+ { -+ try -+ { -+ for (int x = 0; x < GeoStructure.REGION_CELLS_X; x++) -+ { -+ for (int y = 0; y < GeoStructure.REGION_CELLS_Y; y++) -+ { -+ // get block -+ final ABlock block = _blocks[x / GeoStructure.BLOCK_CELLS_X][y / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // skip flat blocks -+ if (block instanceof BlockFlat) -+ { -+ continue; -+ } -+ -+ // for complex and multilayer blocks go though all layers -+ short height = Short.MAX_VALUE; -+ int index; -+ while ((index = block.getIndexBelow(x, y, height)) != -1) -+ { -+ // get height and nswe -+ height = block.getHeight(index); -+ byte nswe = block.getNswe(index); -+ -+ // update nswe with diagonal flags -+ nswe = updateNsweBelow(x, y, height, nswe); -+ -+ // set nswe of the cell -+ block.setNswe(index, nswe); -+ } -+ } -+ } -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ /** -+ * Updates the NSWE flag with diagonal flags. -+ * @param x : Geodata X coordinate. -+ * @param y : Geodata Y coordinate. -+ * @param z : Geodata Z coordinate. -+ * @param nsweValue : NSWE flag to be updated. -+ * @return byte : Updated NSWE flag. -+ */ -+ private static byte updateNsweBelow(int x, int y, short z, byte nsweValue) -+ { -+ byte nswe = nsweValue; -+ -+ // calculate virtual layer height -+ final short height = (short) (z + GeoStructure.CELL_IGNORE_HEIGHT); -+ -+ // get NSWE of neighbor cells below virtual layer (NPC/PC can fall down of clif, but can not climb it -> NSWE of cell below) -+ final byte nsweN = getNsweBelow(x, y - 1, height); -+ final byte nsweS = getNsweBelow(x, y + 1, height); -+ final byte nsweW = getNsweBelow(x - 1, y, height); -+ final byte nsweE = getNsweBelow(x + 1, y, height); -+ -+ // north-west -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NW; -+ } -+ -+ // north-east -+ if ((((nswe & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_N) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_NE; -+ } -+ -+ // south-west -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) || (((nswe & GeoStructure.CELL_FLAG_W) != 0) && ((nsweW & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SW; -+ } -+ -+ // south-east -+ if ((((nswe & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) || (((nswe & GeoStructure.CELL_FLAG_E) != 0) && ((nsweE & GeoStructure.CELL_FLAG_S) != 0))) -+ { -+ nswe |= GeoStructure.CELL_FLAG_SE; -+ } -+ -+ return nswe; -+ } -+ -+ private static byte getNsweBelow(int geoX, int geoY, short worldZ) -+ { -+ // out of geo coordinates -+ if ((geoX < 0) || (geoX >= GeoStructure.REGION_CELLS_X)) -+ { -+ return 0; -+ } -+ -+ // out of geo coordinates -+ if ((geoY < 0) || (geoY >= GeoStructure.REGION_CELLS_Y)) -+ { -+ return 0; -+ } -+ -+ // get block -+ final ABlock block = _blocks[geoX / GeoStructure.BLOCK_CELLS_X][geoY / GeoStructure.BLOCK_CELLS_Y]; -+ -+ // get index, when valid, return nswe -+ final int index = block.getIndexBelow(geoX, geoY, worldZ); -+ return index == -1 ? 0 : block.getNswe(index); -+ } -+ -+ /** -+ * Save region file to file. -+ * @param filename : The name of file to save. -+ * @return boolean : True when successful. -+ */ -+ private static boolean saveGeoBlocks(String filename) -+ { -+ try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(Config.GEODATA_PATH + filename), GeoStructure.REGION_BLOCKS * GeoStructure.BLOCK_CELLS * 3)) -+ { -+ // loop over region blocks and save each block -+ for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) -+ { -+ for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) -+ { -+ _blocks[ix][iy].saveBlock(bos); -+ } -+ } -+ -+ // flush data to file -+ bos.flush(); -+ -+ return true; -+ } -+ catch (Exception e) -+ { -+ return false; -+ } -+ } -+ -+ private static void loadGeoengineConfigs() -+ { -+ final PropertiesParser geoData = new PropertiesParser(Config.GEOENGINE_CONFIG_FILE); -+ Config.GEODATA_PATH = geoData.getString("GeoDataPath", "./data/geodata/"); -+ Config.COORD_SYNCHRONIZE = geoData.getInt("CoordSynchronize", -1); -+ Config.PART_OF_CHARACTER_HEIGHT = geoData.getInt("PartOfCharacterHeight", 75); -+ Config.MAX_OBSTACLE_HEIGHT = geoData.getInt("MaxObstacleHeight", 32); -+ Config.PATHFINDING = geoData.getBoolean("PathFinding", true); -+ Config.PATHFIND_BUFFERS = geoData.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); -+ Config.BASE_WEIGHT = geoData.getInt("BaseWeight", 10); -+ Config.DIAGONAL_WEIGHT = geoData.getInt("DiagonalWeight", 14); -+ Config.OBSTACLE_MULTIPLIER = geoData.getInt("ObstacleMultiplier", 10); -+ Config.HEURISTIC_WEIGHT = geoData.getInt("HeuristicWeight", 20); -+ Config.MAX_ITERATIONS = geoData.getInt("MaxIterations", 3500); -+ } -+} -\ No newline at end of file diff --git a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/geodata/Readme.txt b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/geodata/Readme.txt index a480c8c380..2611882e56 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/geodata/Readme.txt +++ b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/geodata/Readme.txt @@ -23,8 +23,8 @@ a - Prerequisites b - Make it work ---------------------------------------------- -To make geodata working: -* unpack your geodata files into "/data/geodata" folder -* open "/config/GeoEngine.ini" with your favorite text editor and then edit following config: - - CoordSynchronize = 2 -* If you do not use any geodata files, the server will automatically change this setting to -1. +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. diff --git a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/ai/others/FleeMonsters.java b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/ai/others/FleeMonsters.java index 4562336103..a971742033 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/ai/others/FleeMonsters.java +++ b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/ai/others/FleeMonsters.java @@ -59,7 +59,7 @@ public class FleeMonsters extends AbstractNpcAI final int posX = (int) (npc.getX() + (FLEE_DISTANCE * Math.cos(radians))); final int posY = (int) (npc.getY() + (FLEE_DISTANCE * Math.sin(radians))); final int posZ = npc.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, attacker.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), posX, posY, posZ, npc.getInstanceWorld()); npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); return super.onAttack(npc, attacker, damage, isSummon); } diff --git a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/ai/others/RandomWalkingGuards.java b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/ai/others/RandomWalkingGuards.java index b99becfdc6..780502d5d6 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/ai/others/RandomWalkingGuards.java +++ b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/ai/others/RandomWalkingGuards.java @@ -55,7 +55,7 @@ public class RandomWalkingGuards extends AbstractNpcAI if (!npc.isInCombat()) { final Location randomLoc = Util.getRandomPosition(npc.getSpawn().getLocation(), 0, Config.MAX_DRIFT_RANGE); - addMoveToDesire(npc, GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); + addMoveToDesire(npc, GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), randomLoc.getX(), randomLoc.getY(), randomLoc.getZ(), npc.getInstanceWorld()), 23); } startQuestTimer("RANDOM_WALK", getRandom(MIN_WALK_DELAY, MAX_WALK_DELAY), npc, null); } diff --git a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java index 524652cbc1..18511c6541 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java +++ b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/admincommandhandlers/AdminGeodata.java @@ -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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.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.getInstance().getGeoX(worldX); - final int geoY = GeoEngine.getInstance().getGeoY(worldY); + final int geoX = GeoEngine.getGeoX(worldX); + final int geoY = GeoEngine.getGeoY(worldY); if (GeoEngine.getInstance().hasGeoPos(geoX, geoY)) { @@ -133,8 +133,8 @@ public class AdminGeodata implements IAdminCommandHandler } case "admin_geomap": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; BuilderUtil.sendSysMessage(activeChar, "GeoMap: " + x + "_" + y + " (" + ((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + "," + ((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + " to " + ((((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + "," + ((((y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE) + World.TILE_SIZE) - 1) + ")"); break; } diff --git a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java index f3dffa1227..76572f7f25 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java +++ b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/admincommandhandlers/AdminMissingHtmls.java @@ -57,8 +57,8 @@ public class AdminMissingHtmls implements IAdminCommandHandler { case "admin_geomap_missing_htmls": { - final int x = ((activeChar.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((activeChar.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((activeChar.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((activeChar.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final int topLeftX = (x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE; final int topLeftY = (y - World.TILE_ZERO_COORD_Y) * World.TILE_SIZE; final int bottomRightX = (((x - World.TILE_ZERO_COORD_X) * World.TILE_SIZE) + World.TILE_SIZE) - 1; diff --git a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java index fdb2319138..a0a34f1051 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java +++ b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/admincommandhandlers/AdminPathNode.java @@ -19,9 +19,9 @@ package handlers.admincommandhandlers; import java.util.List; import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; +import org.l2jmobius.gameserver.geoengine.GeoEngine; import org.l2jmobius.gameserver.handler.IAdminCommandHandler; +import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.util.BuilderUtil; @@ -44,13 +44,13 @@ public class AdminPathNode implements IAdminCommandHandler } if (activeChar.getTarget() != null) { - final List path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); + final List path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld()); if (path == null) { BuilderUtil.sendSysMessage(activeChar, "No Route!"); return true; } - for (AbstractNodeLoc a : path) + for (Location a : path) { BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ()); } diff --git a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/Blink.java b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/Blink.java index 22f2c84eae..22697e8e20 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/Blink.java +++ b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/Blink.java @@ -88,7 +88,7 @@ public class Blink extends AbstractEffect final int y = effected.getY() + y1; final int z = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, destination, _flyType, _flySpeed, _flyDelay, _animationSpeed)); diff --git a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/Fear.java b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/Fear.java index 95a5afdf98..88c795e04b 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/Fear.java +++ b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/Fear.java @@ -100,7 +100,7 @@ public class Fear extends AbstractEffect final int posY = (int) (effected.getY() + (FEAR_RANGE * Math.sin(radians))); final int posZ = effected.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), posX, posY, posZ, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination); } } diff --git a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java index fddd94c60a..55c2e78d8a 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java +++ b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/FlyAway.java @@ -57,7 +57,7 @@ public class FlyAway extends AbstractEffect final int y = (int) (effector.getY() - (nRadius * (dy / distance))); final int z = effector.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.broadcastPacket(new FlyToLocation(effected, destination, FlyType.THROW_UP)); effected.setXYZ(destination); diff --git a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java index 1fbd8239b0..89ad8670dc 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java +++ b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/KnockBack.java @@ -133,7 +133,7 @@ public class KnockBack extends AbstractEffect final int x = (int) (effected.getX() + (_distance * Math.cos(radians))); final int y = (int) (effected.getY() + (_distance * Math.sin(radians))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effected.getX(), effected.getY(), effected.getZ(), x, y, z, effected.getInstanceWorld()); effected.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effected.broadcastPacket(new FlyToLocation(effected, loc, _type, _speed, _delay, _animationSpeed)); diff --git a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/PullBack.java b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/PullBack.java index ed6a37eff6..38fe2fe9d0 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/PullBack.java +++ b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/PullBack.java @@ -73,7 +73,7 @@ public class PullBack extends AbstractEffect } // In retail, you get debuff, but you are not even moved if there is obstacle. You are still disabled from using skills and moving though. - if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effector.getInstanceWorld())) + if (GeoEngine.getInstance().canMoveToTarget(effected.getX(), effected.getY(), effected.getZ(), effector.getX(), effector.getY(), effector.getZ(), effected.getInstanceWorld())) { effected.broadcastPacket(new FlyToLocation(effected, effector, _type, _speed, _delay, _animationSpeed)); effected.setXYZ(effector.getX(), effector.getY(), GeoEngine.getInstance().getHeight(effector.getX(), effector.getY(), effector.getZ()) + 10); diff --git a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java index d6f9664141..813e496c47 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java +++ b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/TeleportToSummon.java @@ -87,7 +87,7 @@ public class TeleportToSummon extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = summon.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java index d3c263978c..c9dc34ac22 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java +++ b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/effecthandlers/TeleportToTarget.java @@ -76,7 +76,7 @@ public class TeleportToTarget extends AbstractEffect final int y = (int) (py + (25 * Math.sin(ph))); final int z = effected.getZ(); - final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(effector.getX(), effector.getY(), effector.getZ(), x, y, z, effector.getInstanceWorld()); + final Location loc = GeoEngine.getInstance().getValidLocation(effector.getX(), effector.getY(), effector.getZ(), x, y, z,effector.getInstanceWorld()); effector.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); effector.broadcastPacket(new FlyToLocation(effector, loc.getX(), loc.getY(), loc.getZ(), FlyType.DUMMY)); diff --git a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java index 99fbcb99ed..ffccf7fc7d 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java +++ b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/itemhandlers/RollingDice.java @@ -65,7 +65,7 @@ public class RollingDice implements IItemHandler final int x = player.getX() + x1; final int y = player.getY() + y1; final int z = player.getZ(); - final Location destination = GeoEngine.getInstance().canMoveToTargetLoc(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); + final Location destination = GeoEngine.getInstance().getValidLocation(player.getX(), player.getY(), player.getZ(), x, y, z, player.getInstanceWorld()); Broadcast.toSelfAndKnownPlayers(player, new Dice(player.getObjectId(), itemId, number, destination.getX(), destination.getY(), destination.getZ())); final SystemMessage sm = new SystemMessage(SystemMessageId.C1_HAS_ROLLED_A_S2); diff --git a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/targethandlers/Ground.java b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/targethandlers/Ground.java index 82ab321c29..d8c73627f5 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/targethandlers/Ground.java +++ b/L2J_Mobius_Essence_5.0_Sylph/dist/game/data/scripts/handlers/targethandlers/Ground.java @@ -52,7 +52,7 @@ public class Ground implements ITargetTypeHandler return null; } - if (!GeoEngine.getInstance().canSeeTarget(creature, worldPosition)) + if (!GeoEngine.getInstance().canSeeLocation(creature, worldPosition)) { if (sendMessage) { diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/Config.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/Config.java index f96967cac1..50e2010a2a 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/Config.java +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/Config.java @@ -61,6 +61,7 @@ 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; @@ -928,12 +929,17 @@ public class Config // GeoEngine // -------------------------------------------------- public static Path GEODATA_PATH; + public static GeoType GEODATA_TYPE; public static boolean PATHFINDING; public static String PATHFIND_BUFFERS; - public static float LOW_WEIGHT; - public static float MEDIUM_WEIGHT; - public static float HIGH_WEIGHT; - public static float DIAGONAL_WEIGHT; + public static int MOVE_WEIGHT; + public static int MOVE_WEIGHT_DIAG; + public static int OBSTACLE_WEIGHT; + 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; /** Attribute System */ public static int S_WEAPON_STONE; @@ -2467,12 +2473,17 @@ public class Config // Load GeoEngine config file (if exists) final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE); GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata")); + GEODATA_TYPE = Enum.valueOf(GeoType.class, GeoEngine.getString("GeoDataType", "L2J")); PATHFINDING = GeoEngine.getBoolean("PathFinding", true); - PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2"); - LOW_WEIGHT = GeoEngine.getFloat("LowWeight", 0.5f); - MEDIUM_WEIGHT = GeoEngine.getFloat("MediumWeight", 2); - HIGH_WEIGHT = GeoEngine.getFloat("HighWeight", 3); - DIAGONAL_WEIGHT = GeoEngine.getFloat("DiagonalWeight", 0.707f); + PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "500x10;1000x10;3000x5;5000x3;10000x3"); + MOVE_WEIGHT = GeoEngine.getInt("MoveWeight", 10); + MOVE_WEIGHT_DIAG = GeoEngine.getInt("MoveWeightDiag", 14); + OBSTACLE_WEIGHT = GeoEngine.getInt("ObstacleWeight", 30); + HEURISTIC_WEIGHT = GeoEngine.getInt("HeuristicWeight", 12); + HEURISTIC_WEIGHT_DIAG = GeoEngine.getInt("HeuristicWeightDiag", 18); + MAX_ITERATIONS = GeoEngine.getInt("MaxIterations", 3500); + PART_OF_CHARACTER_HEIGHT = GeoEngine.getInt("PartOfCharacterHeight", 75); + MAX_OBSTACLE_HEIGHT = GeoEngine.getInt("MaxObstacleHeight", 32); // Load AllowedPlayerRaces config file (if exists) final PropertiesParser AllowedPlayerRaces = new PropertiesParser(CUSTOM_ALLOWED_PLAYER_RACES_CONFIG_FILE); diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/commons/util/CommonUtil.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/commons/util/CommonUtil.java index 29a88cf20c..86fa98ff10 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/commons/util/CommonUtil.java +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/commons/util/CommonUtil.java @@ -591,6 +591,17 @@ public class CommonUtil return formatter.format(value); } + /** + * @param numToTest : The number to test. + * @param min : The minimum limit. + * @param max : The maximum limit. + * @return the number or one of the limit (mininum / maximum). + */ + public static int limit(int numToTest, int min, int max) + { + return (numToTest > max) ? max : ((numToTest < min) ? min : numToTest); + } + public static boolean isNullOrEmpty(CharSequence value) { return isNull(value) || (value.length() == 0); diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/commons/util/Point2D.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/commons/util/Point2D.java new file mode 100644 index 0000000000..ebb6272e5e --- /dev/null +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/commons/util/Point2D.java @@ -0,0 +1,180 @@ +/* + * 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 . + */ +package org.l2jmobius.commons.util; + +import java.util.Objects; + +/** + * A datatype used to retain a 2D (x/y) point. It got the capability to be set and cleaned. + */ +public class Point2D +{ + protected volatile int _x; + protected volatile int _y; + + public Point2D(int x, int y) + { + _x = x; + _y = y; + } + + @Override + public Point2D clone() + { + return new Point2D(_x, _y); + } + + @Override + public String toString() + { + return _x + ", " + _y; + } + + @Override + public int hashCode() + { + return Objects.hash(_x, _y); + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + + if (obj == null) + { + return false; + } + + if (getClass() != obj.getClass()) + { + return false; + } + + final Point2D other = (Point2D) obj; + return (_x == other._x) && (_y == other._y); + } + + /** + * @param x : The X coord to test. + * @param y : The Y coord to test. + * @return True if all coordinates equals this {@link Point2D} coordinates. + */ + public boolean equals(int x, int y) + { + return (_x == x) && (_y == y); + } + + public int getX() + { + return _x; + } + + public void setX(int x) + { + _x = x; + } + + public int getY() + { + return _y; + } + + public void setY(int y) + { + _y = y; + } + + public void set(int x, int y) + { + _x = x; + _y = y; + } + + /** + * Refresh the current {@link Point2D} using a reference {@link Point2D} and a distance. The new destination is calculated to go in opposite side of the {@link Point2D} reference.
+ *
+ * This method is perfect to calculate fleeing characters position. + * @param referenceLoc : The Point2D used as position. + * @param distance : The distance to be set between current and new position. + */ + public void setFleeing(Point2D referenceLoc, int distance) + { + final double xDiff = referenceLoc.getX() - _x; + final double yDiff = referenceLoc.getY() - _y; + + final double yxRation = Math.abs(xDiff / yDiff); + + final int y = (int) (distance / (yxRation + 1)); + final int x = (int) (y * yxRation); + + _x += (xDiff < 0 ? x : -x); + _y += (yDiff < 0 ? y : -y); + } + + public void clean() + { + _x = 0; + _y = 0; + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @return The distance between this {@Point2D} and some given coordinates. + */ + public double distance2D(int x, int y) + { + final double dx = (double) _x - x; + final double dy = (double) _y - y; + + return Math.sqrt((dx * dx) + (dy * dy)); + } + + /** + * @param point : The {@link Point2D} to test. + * @return The distance between this {@Point2D} and the {@link Point2D} set as parameter. + */ + public double distance2D(Point2D point) + { + return distance2D(point.getX(), point.getY()); + } + + /** + * @param x : The X position to test. + * @param y : The Y position to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of some given coordinates. + */ + public boolean isIn2DRadius(int x, int y, int radius) + { + return distance2D(x, y) < radius; + } + + /** + * @param point : The Point2D to test. + * @param radius : The radius to check. + * @return True if this {@link Point2D} is in the radius of the {@link Point2D} set as parameter. + */ + public boolean isIn2DRadius(Point2D point, int radius) + { + return distance2D(point) < radius; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/ai/AttackableAI.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/ai/AttackableAI.java index 547963e3e8..dc29d4cf6e 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/ai/AttackableAI.java +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/ai/AttackableAI.java @@ -578,7 +578,7 @@ public class AttackableAI extends CreatureAI } // Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast) - final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); + final Location moveLoc = _actor.isFlying() ? new Location(x1, y1, z1) : GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), x1, y1, z1, npc.getInstanceWorld()); moveTo(moveLoc.getX(), moveLoc.getY(), moveLoc.getZ()); } } @@ -801,7 +801,7 @@ public class AttackableAI extends CreatureAI final int newZ = npc.getZ() + 30; // Mobius: Verify destination. Prevents wall collision issues and fixes monsters not avoiding obstacles. - moveTo(GeoEngine.getInstance().canMoveToTargetLoc(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); + moveTo(GeoEngine.getInstance().getValidLocation(npc.getX(), npc.getY(), npc.getZ(), newX, newY, newZ, npc.getInstanceWorld())); } return; } diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/data/SpawnTable.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/data/SpawnTable.java index a81201f69e..19f0db5b3e 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/data/SpawnTable.java +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/data/SpawnTable.java @@ -114,8 +114,8 @@ public class SpawnTable } // XML file for spawn - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); // Write info to XML @@ -192,8 +192,8 @@ public class SpawnTable if (update) { - final int x = ((spawn.getX() - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((spawn.getY() - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((spawn.getX() - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((spawn.getY() - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; final File spawnFile = spawn.getNpcSpawnTemplate() != null ? spawn.getNpcSpawnTemplate().getSpawnTemplate().getFile() : new File(OTHER_XML_FOLDER + "/" + x + "_" + y + ".xml"); final File tempFile = new File(spawnFile.getAbsolutePath().substring(Config.DATAPACK_ROOT.getAbsolutePath().length() + 1).replace('\\', '/') + ".tmp"); try diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/enums/GeoType.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/enums/GeoType.java new file mode 100644 index 0000000000..f77aa5eac4 --- /dev/null +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/enums/GeoType.java @@ -0,0 +1,35 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.enums; + +public enum GeoType +{ + L2J("%d_%d.l2j"), + L2OFF("%d_%d_conv.dat"); + + private final String _filename; + + private GeoType(String filename) + { + _filename = filename; + } + + public String getFilename() + { + return _filename; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java new file mode 100644 index 0000000000..e62f50a8df --- /dev/null +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/enums/MoveDirectionType.java @@ -0,0 +1,144 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java index 9d255a5f54..7bef9c770b 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/GeoEngine.java @@ -16,72 +16,63 @@ */ package org.l2jmobius.gameserver.geoengine; -import java.io.IOException; import java.io.RandomAccessFile; import java.nio.ByteOrder; -import java.nio.channels.FileChannel.MapMode; +import java.nio.MappedByteBuffer; +import java.nio.channels.FileChannel; import java.nio.file.Files; import java.nio.file.Path; -import java.util.concurrent.atomic.AtomicReferenceArray; -import java.util.logging.Level; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; import java.util.logging.Logger; import org.l2jmobius.Config; +import org.l2jmobius.commons.util.CommonUtil; import org.l2jmobius.gameserver.data.xml.DoorData; import org.l2jmobius.gameserver.data.xml.FenceData; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; -import org.l2jmobius.gameserver.geoengine.geodata.IRegion; -import org.l2jmobius.gameserver.geoengine.geodata.NullRegion; -import org.l2jmobius.gameserver.geoengine.geodata.Region; +import org.l2jmobius.gameserver.enums.GeoType; +import org.l2jmobius.gameserver.enums.MoveDirectionType; +import org.l2jmobius.gameserver.geoengine.geodata.ABlock; +import org.l2jmobius.gameserver.geoengine.geodata.BlockComplex; +import org.l2jmobius.gameserver.geoengine.geodata.BlockFlat; +import org.l2jmobius.gameserver.geoengine.geodata.BlockMultilayer; +import org.l2jmobius.gameserver.geoengine.geodata.BlockNull; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; +import org.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer; import org.l2jmobius.gameserver.model.Location; import org.l2jmobius.gameserver.model.World; import org.l2jmobius.gameserver.model.WorldObject; +import org.l2jmobius.gameserver.model.actor.Creature; import org.l2jmobius.gameserver.model.instancezone.Instance; -import org.l2jmobius.gameserver.model.interfaces.ILocational; -import org.l2jmobius.gameserver.util.GeoUtils; -import org.l2jmobius.gameserver.util.LinePointIterator; -import org.l2jmobius.gameserver.util.LinePointIterator3D; -/** - * @author -Nemesiss-, HorridoJoho - */ public class GeoEngine { - private static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); - - private static final int WORLD_MIN_X = -655360; - private static final int WORLD_MIN_Y = -589824; - private static final int WORLD_MIN_Z = -16384; - - /** 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; - - /** The regions array */ - private final AtomicReferenceArray _regions = new AtomicReferenceArray<>(GEO_REGIONS); + protected static final Logger LOGGER = Logger.getLogger(GeoEngine.class.getName()); private static final String FILE_NAME_FORMAT = "%d_%d.l2j"; - private static final int ELEVATED_SEE_OVER_DISTANCE = 2; - private static final int MAX_SEE_OVER_HEIGHT = 48; - protected GeoEngine() + private final ABlock[][] _blocks; + private final BlockNull _nullBlock; + + // Pre-allocated buffers. + private final BufferHolder[] _buffers; + + public GeoEngine() { LOGGER.info("GeoEngine: Initializing..."); - for (int i = 0; i < _regions.length(); i++) - { - _regions.set(i, NullRegion.INSTANCE); - } + // Initialize block container. + _blocks = new ABlock[GeoStructure.GEO_BLOCKS_X][GeoStructure.GEO_BLOCKS_Y]; + + // Load null block. + _nullBlock = new BlockNull(); + + // Initialize multilayer temporarily buffer. + BlockMultilayer.initialize(); + + // Load geo files according to geoengine config setup. int loaded = 0; try { @@ -92,23 +83,52 @@ public class GeoEngine final Path geoFilePath = Config.GEODATA_PATH.resolve(String.format(FILE_NAME_FORMAT, regionX, regionY)); if (Files.exists(geoFilePath)) { - try (RandomAccessFile raf = new RandomAccessFile(geoFilePath.toFile(), "r")) + // Region file is load-able, try to load it. + if (loadGeoBlocks(regionX, regionY)) { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, new Region(raf.getChannel().map(MapMode.READ_ONLY, 0, raf.length()).load().order(ByteOrder.LITTLE_ENDIAN))); loaded++; } } + else + { + // Region file is not load-able, load null blocks. + loadNullBlocks(regionX, regionY); + } } } } catch (Exception e) { - LOGGER.log(Level.SEVERE, "GeoEngine: Failed to load geodata!", e); + LOGGER.warning("GeoEngine: Failed to load geodata! " + e); System.exit(1); } - LOGGER.info("GeoEngine: Loaded " + loaded + " geodata files."); + // Release multilayer block temporarily buffer. + BlockMultilayer.release(); + + String[] array = Config.PATHFIND_BUFFERS.split(";"); + _buffers = new BufferHolder[array.length]; + + int count = 0; + for (int i = 0; i < array.length; i++) + { + String buf = array[i]; + String[] args = buf.split("x"); + + try + { + int size = Integer.parseInt(args[1]); + count += size; + _buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size); + } + catch (Exception e) + { + LOGGER.warning("Could not load buffer setting:" + buf + ". " + e); + } + } + LOGGER.info("Loaded " + count + " node buffers."); + // Avoid wrong configs when no files are loaded. if ((loaded == 0) && Config.PATHFINDING) { @@ -118,616 +138,913 @@ public class GeoEngine } /** - * @param geoX - * @param geoY - * @return the region + * Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer and log this situation. + * @param size : pre-calculated minimal required size + * @return NodeBuffer : buffer */ - private IRegion getRegion(int geoX, int geoY) + private NodeBuffer getBuffer(int size) { - final int region = ((geoX / IRegion.REGION_CELLS_X) * GEO_REGIONS_Y) + (geoY / IRegion.REGION_CELLS_Y); - if ((region < 0) || (region >= _regions.length())) + NodeBuffer current = null; + for (BufferHolder holder : _buffers) { - return null; - } - return _regions.get(region); - } - - /** - * @param filePath - * @param regionX - * @param regionY - * @throws IOException - */ - 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()).load().order(ByteOrder.LITTLE_ENDIAN))); - } - } - - /** - * @param regionX - * @param regionY - */ - public void unloadRegion(int regionX, int regionY) - { - _regions.set((regionX * GEO_REGIONS_Y) + regionY, NullRegion.INSTANCE); - } - - /** - * @param geoX - * @param geoY - * @return if geodata exist - */ - public boolean hasGeoPos(int geoX, int geoY) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return false; - } - return region.hasGeo(); - } - - /** - * Checks the specified position for available geodata. - * @param x the world x - * @param y the world y - * @return {@code true} if there is geodata for the given coordinates, {@code false} otherwise - */ - public boolean hasGeo(int x, int y) - { - return hasGeoPos(getGeoX(x), getGeoY(y)); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe check - */ - public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return true; - } - return region.checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @param nswe - * @return the nearest nswe anti-corner cut - */ - public boolean checkNearestNsweAntiCornerCut(int geoX, int geoY, int worldZ, int nswe) - { - boolean can = true; - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST)) - { - can = checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX, geoY - 1, worldZ, Cell.NSWE_NORTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_EAST) && checkNearestNswe(geoX + 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - if (can && ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST)) - { - can = checkNearestNswe(geoX, geoY + 1, worldZ, Cell.NSWE_WEST) && checkNearestNswe(geoX - 1, geoY, worldZ, Cell.NSWE_SOUTH); - } - return can && checkNearestNswe(geoX, geoY, worldZ, nswe); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the nearest Z value - */ - public int getNearestZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNearestZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next lower Z value - */ - public int getNextLowerZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextLowerZ(geoX, geoY, worldZ); - } - - /** - * @param geoX - * @param geoY - * @param worldZ - * @return the next higher Z value - */ - public int getNextHigherZ(int geoX, int geoY, int worldZ) - { - final IRegion region = getRegion(geoX, geoY); - if (region == null) - { - return worldZ; - } - return region.getNextHigherZ(geoX, geoY, worldZ); - } - - /** - * Gets the Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHeight(int x, int y, int z) - { - return getNearestZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next lower Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getLowerHeight(int x, int y, int z) - { - return getNextLowerZ(getGeoX(x), getGeoY(y), z); - } - - /** - * Gets the next higher Z height. - * @param x the world x - * @param y the world y - * @param z the world z - * @return the nearest Z height - */ - public int getHigherHeight(int x, int y, int z) - { - return getNextHigherZ(getGeoX(x), getGeoY(y), z); - } - - /** - * @param worldX - * @return the geo X - */ - public int getGeoX(int worldX) - { - return (worldX - WORLD_MIN_X) / 16; - } - - /** - * @param worldY - * @return the geo Y - */ - public int getGeoY(int worldY) - { - return (worldY - WORLD_MIN_Y) / 16; - } - - /** - * @param worldZ - * @return the geo Z - */ - public int getGeoZ(int worldZ) - { - return (worldZ - WORLD_MIN_Z) / 16; - } - - /** - * @param geoX - * @return the world X - */ - public int getWorldX(int geoX) - { - return (geoX * 16) + WORLD_MIN_X + 8; - } - - /** - * @param geoY - * @return the world Y - */ - public int getWorldY(int geoY) - { - return (geoY * 16) + WORLD_MIN_Y + 8; - } - - /** - * @param geoZ - * @return the world Z - */ - public int getWorldZ(int geoZ) - { - return (geoZ * 16) + WORLD_MIN_Z + 8; - } - - /** - * Can see target. Doors as target always return true. Checks doors between. - * @param cha the character - * @param target the target - * @return {@code true} if the character can see the target (LOS), {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, WorldObject target) - { - if (target.isDoor()) - { - // Can always see doors. - return true; - } - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), target.getX(), target.getY(), target.getZ(), target.getInstanceWorld()); - } - - /** - * Can see target. Checks doors between. - * @param cha the character - * @param worldPosition the world position - * @return {@code true} if the character can see the target at the given world position, {@code false} otherwise - */ - public boolean canSeeTarget(WorldObject cha, ILocational worldPosition) - { - return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), cha.getInstanceWorld(), worldPosition.getX(), worldPosition.getY(), worldPosition.getZ()); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param world - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @param tworld the target's instanceId - * @return - */ - public boolean canSeeTarget(int x, int y, int z, Instance world, int tx, int ty, int tz, Instance tworld) - { - return (world != tworld) ? false : canSeeTarget(x, y, z, world, tx, ty, tz); - } - - /** - * Can see target. Checks doors between. - * @param x the x coordinate - * @param y the y coordinate - * @param z the z coordinate - * @param instance - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tz the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int x, int y, int z, Instance instance, int tx, int ty, int tz) - { - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, true)) - { - return false; - } - return canSeeTarget(x, y, z, tx, ty, tz); - } - - /** - * @param prevX - * @param prevY - * @param prevGeoZ - * @param curX - * @param curY - * @param nswe - * @return the LOS Z value - */ - private int getLosGeoZ(int prevX, int prevY, int prevGeoZ, int curX, int curY, int nswe) - { - if ((((nswe & Cell.NSWE_NORTH) != 0) && ((nswe & Cell.NSWE_SOUTH) != 0)) || (((nswe & Cell.NSWE_WEST) != 0) && ((nswe & Cell.NSWE_EAST) != 0))) - { - throw new RuntimeException("Multiple directions!"); - } - - if (checkNearestNsweAntiCornerCut(prevX, prevY, prevGeoZ, nswe)) - { - return getNearestZ(curX, curY, prevGeoZ); - } - - return getNextHigherZ(curX, curY, prevGeoZ); - } - - /** - * Can see target. Does not check doors between. - * @param xValue the x coordinate - * @param yValue the y coordinate - * @param zValue the z coordinate - * @param txValue the target's x coordinate - * @param tyValue the target's y coordinate - * @param tzValue the target's z coordinate - * @return {@code true} if there is line of sight between the given coordinate sets, {@code false} otherwise - */ - public boolean canSeeTarget(int xValue, int yValue, int zValue, int txValue, int tyValue, int tzValue) - { - int x = xValue; - int y = yValue; - int tx = txValue; - int ty = tyValue; - - int geoX = getGeoX(x); - int geoY = getGeoY(y); - int tGeoX = getGeoX(tx); - int tGeoY = getGeoY(ty); - - int z = getNearestZ(geoX, geoY, zValue); - int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - // Fastpath. - if ((geoX == tGeoX) && (geoY == tGeoY)) - { - if (hasGeoPos(tGeoX, tGeoY)) - { - return z == tz; - } - return true; - } - - if (tz > z) - { - int tmp = tx; - tx = x; - x = tmp; - - tmp = ty; - ty = y; - y = tmp; - - tmp = tz; - tz = z; - z = tmp; - - tmp = tGeoX; - tGeoX = geoX; - geoX = tmp; - - tmp = tGeoY; - tGeoY = geoY; - geoY = tmp; - } - - final LinePointIterator3D pointIter = new LinePointIterator3D(geoX, geoY, z, tGeoX, tGeoY, tz); - // First point is guaranteed to be available, skip it, we can always see our own position. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - final int prevZ = pointIter.z(); - int prevGeoZ = prevZ; - int ptIndex = 0; - while (pointIter.next()) - { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - - if ((curX == prevX) && (curY == prevY)) + // Find proper size of buffer. + if (holder._size < size) { continue; } - final int beeCurZ = pointIter.z(); - int curGeoZ = prevGeoZ; - - // Check if the position has geodata. - if (hasGeoPos(curX, curY)) + // Find unlocked NodeBuffer. + for (NodeBuffer buffer : holder._buffer) { - final int beeCurGeoZ = getNearestZ(curX, curY, beeCurZ); - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); // .computeDirection(prevX, prevY, curX, curY); - curGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, curX, curY, nswe); - int maxHeight; - if (ptIndex < ELEVATED_SEE_OVER_DISTANCE) + if (!buffer.isLocked()) { - maxHeight = z + MAX_SEE_OVER_HEIGHT; - } - else - { - maxHeight = beeCurZ + MAX_SEE_OVER_HEIGHT; + continue; } - boolean canSeeThrough = false; - if ((curGeoZ <= maxHeight) && (curGeoZ <= beeCurGeoZ)) + return buffer; + } + + LOGGER.warning("Creating new NodeBuffer " + size + " size, as no buffer empty or out of size."); + + // NodeBuffer not found, allocate temporary buffer. + current = new NodeBuffer(holder._size); + current.isLocked(); + } + + return current; + } + + /** + * Loads geodata from a file. When file does not exist, is corrupted or not consistent, loads none geodata. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + * @return boolean : True, when geodata file was loaded without problem. + */ + private boolean loadGeoBlocks(int regionX, int regionY) + { + final String filename = String.format(Config.GEODATA_TYPE.getFilename(), regionX, regionY); + final String filepath = Config.GEODATA_PATH + "\\" + filename; + + // Standard load. + try (RandomAccessFile raf = new RandomAccessFile(filepath, "r"); + FileChannel fc = raf.getChannel()) + { + // Initialize file buffer. + MappedByteBuffer buffer = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()).load(); + buffer.order(ByteOrder.LITTLE_ENDIAN); + + // Load 18B header for L2OFF geodata (1st and 2nd byte...region X and Y). + if (Config.GEODATA_TYPE == GeoType.L2OFF) + { + for (int i = 0; i < 18; i++) { - if ((nswe & Cell.NSWE_NORTH_EAST) == Cell.NSWE_NORTH_EAST) + buffer.get(); + } + } + + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Loop over region blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + if (Config.GEODATA_TYPE == GeoType.L2J) { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_NORTH_WEST) == Cell.NSWE_NORTH_WEST) - { - final int northGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY - 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_NORTH); - canSeeThrough = (northGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (northGeoZ <= getNearestZ(prevX, prevY - 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_EAST) == Cell.NSWE_SOUTH_EAST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_EAST); - final int eastGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX + 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (eastGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (eastGeoZ <= getNearestZ(prevX + 1, prevY, beeCurZ)); - } - else if ((nswe & Cell.NSWE_SOUTH_WEST) == Cell.NSWE_SOUTH_WEST) - { - final int southGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX, prevY + 1, Cell.NSWE_WEST); - final int westGeoZ = getLosGeoZ(prevX, prevY, prevGeoZ, prevX - 1, prevY, Cell.NSWE_SOUTH); - canSeeThrough = (southGeoZ <= maxHeight) && (westGeoZ <= maxHeight) && (southGeoZ <= getNearestZ(prevX, prevY + 1, beeCurZ)) && (westGeoZ <= getNearestZ(prevX - 1, prevY, beeCurZ)); + // Get block type. + final byte type = buffer.get(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + case GeoStructure.TYPE_MULTILAYER_L2J: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + default: + { + throw new IllegalArgumentException("Unknown block type: " + type); + } + } } else { - canSeeThrough = true; + // Get block type. + final short type = buffer.getShort(); + + // Load block according to block type. + switch (type) + { + case GeoStructure.TYPE_FLAT_L2J_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockFlat(buffer, Config.GEODATA_TYPE); + break; + } + case GeoStructure.TYPE_COMPLEX_L2OFF: + { + _blocks[blockX + ix][blockY + iy] = new BlockComplex(buffer); + break; + } + default: + { + _blocks[blockX + ix][blockY + iy] = new BlockMultilayer(buffer, Config.GEODATA_TYPE); + break; + } + } } } + } + + // Check data consistency. + if (buffer.remaining() > 0) + { + LOGGER.warning("Region file " + filename + " can be corrupted, remaining " + buffer.remaining() + " bytes to read."); + } + + // Loading was successful. + return true; + } + catch (Exception e) + { + // An error occured while loading, load null blocks. + LOGGER.warning("Error loading " + filename + " region file. " + e); + + // Replace whole region file with null blocks. + loadNullBlocks(regionX, regionY); + + // Loading was not successful. + return false; + } + } + + /** + * Loads null blocks. Used when no region file is detected or an error occurs during loading. + * @param regionX : Geodata file region X coordinate. + * @param regionY : Geodata file region Y coordinate. + */ + private void loadNullBlocks(int regionX, int regionY) + { + // Get block indexes. + final int blockX = (regionX - World.TILE_X_MIN) * GeoStructure.REGION_BLOCKS_X; + final int blockY = (regionY - World.TILE_Y_MIN) * GeoStructure.REGION_BLOCKS_Y; + + // Load all null blocks. + for (int ix = 0; ix < GeoStructure.REGION_BLOCKS_X; ix++) + { + for (int iy = 0; iy < GeoStructure.REGION_BLOCKS_Y; iy++) + { + _blocks[blockX + ix][blockY + iy] = _nullBlock; + } + } + } + + /** + * Converts world X to geodata X. + * @param worldX + * @return int : Geo X + */ + public static int getGeoX(int worldX) + { + return (worldX - World.WORLD_X_MIN) >> 4; + } + + /** + * Converts world Y to geodata Y. + * @param worldY + * @return int : Geo Y + */ + public static int getGeoY(int worldY) + { + return (worldY - World.WORLD_Y_MIN) >> 4; + } + + /** + * Converts geodata X to world X. + * @param geoX + * @return int : World X + */ + public static int getWorldX(int geoX) + { + return (geoX << 4) + World.WORLD_X_MIN + 8; + } + + /** + * Converts geodata Y to world Y. + * @param geoY + * @return int : World Y + */ + public static int getWorldY(int geoY) + { + return (geoY << 4) + World.WORLD_Y_MIN + 8; + } + + /** + * Returns block of geodata on given coordinates. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return {@link ABlock} : Block of geodata. + */ + public ABlock getBlock(int geoX, int geoY) + { + final int x = geoX / GeoStructure.BLOCK_CELLS_X; + if ((x < 0) || (x >= GeoStructure.GEO_BLOCKS_X)) + { + return null; + } + final int y = geoY / GeoStructure.BLOCK_CELLS_Y; + if ((y < 0) || (y >= GeoStructure.GEO_BLOCKS_Y)) + { + return null; + } + return _blocks[x][y]; + } + + /** + * Check if geo coordinates has geo. + * @param geoX : Geodata X + * @param geoY : Geodata Y + * @return boolean : True, if given geo coordinates have geodata + */ + public boolean hasGeoPos(int geoX, int geoY) + { + final ABlock block = getBlock(geoX, geoY); + return (block != null) && block.hasGeoPos(); + } + + /** + * Returns the height of cell, which is closest to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell geodata Z coordinate, closest to given coordinates. + */ + public short getHeightNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return (short) worldZ; + } + return block.getHeightNearest(geoX, geoY, worldZ); + } + + /** + * Returns the NSWE flag byte of cell, which is closes to given coordinates. + * @param geoX : Cell geodata X coordinate. + * @param geoY : Cell geodata Y coordinate. + * @param worldZ : Cell world Z coordinate. + * @return short : Cell NSWE flag byte coordinate, closest to given coordinates. + */ + public byte getNsweNearest(int geoX, int geoY, int worldZ) + { + final ABlock block = getBlock(geoX, geoY); + if (block == null) + { + return GeoStructure.CELL_FLAG_ALL; + } + return block.getNsweNearest(geoX, geoY, worldZ); + } + + /** + * Check if world coordinates has geo. + * @param worldX : World X + * @param worldY : World Y + * @return boolean : True, if given world coordinates have geodata + */ + public boolean hasGeo(int worldX, int worldY) + { + return hasGeoPos(getGeoX(worldX), getGeoY(worldY)); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param loc : The location used as reference. + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(Location loc) + { + return getHeightNearest(getGeoX(loc.getX()), getGeoY(loc.getY()), loc.getZ()); + } + + /** + * Returns closest Z coordinate according to geodata. + * @param worldX : world x + * @param worldY : world y + * @param worldZ : world z + * @return short : nearest Z coordinates according to geodata + */ + public short getHeight(int worldX, int worldY, int worldZ) + { + return getHeightNearest(getGeoX(worldX), getGeoY(worldY), worldZ); + } + + /** + * Check line of sight from {@link WorldObject} to {@link WorldObject}.
+ * @param object : The origin object. + * @param target : The target object. + * @return True, when object can see target. + */ + public boolean canSeeTarget(WorldObject object, WorldObject target) + { + // Can always see doors. + if (target.isDoor()) + { + return true; + } + + if (object.getInstanceWorld() != target.getInstanceWorld()) + { + return false; + } + + if (DoorData.getInstance().checkIfDoorsBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(object.getX(), object.getY(), object.getZ(), target.getX(), target.getY(), target.getZ(), object.getInstanceWorld())) + { + return false; + } + + // Get object's and target's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + double theight = 0; + if (target instanceof Creature) + { + theight += (((Creature) target).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + return canSee(object.getX(), object.getY(), object.getZ(), oheight, target.getX(), target.getY(), target.getZ(), theight) && canSee(target.getX(), target.getY(), target.getZ(), theight, object.getX(), object.getY(), object.getZ(), oheight); + } + + /** + * Check line of sight from {@link WorldObject} to {@link Location}.
+ * Note: The check uses {@link Location}'s real Z coordinate (e.g. point above ground), not its geodata representation. + * @param object : The origin object. + * @param position : The target position. + * @return True, when object can see position. + */ + public boolean canSeeLocation(WorldObject object, Location position) + { + // Get object and location coordinates. + int ox = object.getX(); + int oy = object.getY(); + int oz = object.getZ(); + int tx = position.getX(); + int ty = position.getY(); + int tz = position.getZ(); + + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld(), false)) + { + return false; + } + + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, object.getInstanceWorld())) + { + return false; + } + + // Get object's line of sight height (if relevant). + // Note: real creature height = collision height * 2 + double oheight = 0; + if (object instanceof Creature) + { + oheight += (((Creature) object).getCollisionHeight() * 2 * Config.PART_OF_CHARACTER_HEIGHT) / 100; + } + + // Perform geodata check. + return canSee(ox, oy, oz, oheight, tx, ty, tz, 0) && canSee(tx, ty, tz, 0, ox, oy, oz, oheight); + } + + /** + * Simple check for origin to target visibility.
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param oheight : The height of origin, used as start point. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param theight : The height of target, used as end point. + * @return True, when origin can see target. + */ + public boolean canSee(int ox, int oy, int oz, double oheight, int tx, int ty, int tz, double theight) + { + // Get origin geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get target geodata coordinates. + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check being on same cell and layer (index). + // Note: Get index must use origin height increased by cell height, the method returns index to height exclusive self. + int index = block.getIndexBelow(gox, goy, oz + GeoStructure.CELL_HEIGHT); + if (index < 0) + { + return false; + } + + if ((gox == gtx) && (goy == gty)) + { + return index == block.getIndexBelow(gtx, gty, tz + GeoStructure.CELL_HEIGHT); + } + + // Get ground and nswe flag. + int groundZ = block.getHeight(index); + int nswe = block.getNswe(index); + + // Get delta coordinates, slope of line (XY, XZ) and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double dz = (tz + theight) - (oz + oheight); + final double m = (double) dy / dx; + final double mz = dz / Math.sqrt((dx * dx) + (dy * dy)); + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); + + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) + { + // Set next cell in X direction. + gridX += mdt.getStepX(); + gox += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); - if (!canSeeThrough) - { - return false; - } + // Set next cell in Y direction. + gridY += mdt.getStepY(); + goy += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevGeoZ = curGeoZ; - ++ptIndex; + // Get block of the next cell. + block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Get line of sight height (including Z slope). + double losz = oz + oheight + Config.MAX_OBSTACLE_HEIGHT; + losz += mz * Math.sqrt(((checkX - ox) * (checkX - ox)) + ((checkY - oy) * (checkY - oy))); + + // Check line of sight going though wall (vertical check). + + // Get index of particular layer, based on last iterated cell conditions. + boolean canMove = (nswe & dir) != 0; + if (canMove) + { + // No wall present, get next cell below current cell. + index = block.getIndexBelow(gox, goy, groundZ + GeoStructure.CELL_IGNORE_HEIGHT); + } + else + { + // Wall present, get next cell above current cell. + index = block.getIndexAbove(gox, goy, groundZ - (2 * GeoStructure.CELL_HEIGHT)); + } + + // Next cell's does not exist (no geodata with valid condition), return fail. + if (index < 0) + { + return false; + } + + // Get next cell's layer height. + int z = block.getHeight(index); + + // Perform sine of sight check (next cell is above line of sight line), return fail. + if (z > losz) + { + return false; + } + + // Next cell is accessible, update z and NSWE. + groundZ = z; + nswe = block.getNswe(index); } + // Iteration is completed, no obstacle is found. return true; } /** - * Move check. - * @param x the x coordinate - * @param y the y coordinate - * @param zValue the z coordinate - * @param tx the target's x coordinate - * @param ty the target's y coordinate - * @param tzValue the target's z coordinate - * @param instance the instance - * @return the last Location (x,y,z) where player can walk - just before wall + * Check movement from coordinates to coordinates.
+ * Note: The Z coordinates are supposed to be already validated geodata coordinates. + * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return True, when target coordinates are reachable from origin coordinates. */ - public Location canMoveToTargetLoc(int x, int y, int zValue, int tx, int ty, int tzValue, Instance instance) + public boolean canMoveToTarget(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(x); - final int geoY = getGeoY(y); - final int z = getNearestZ(geoX, geoY, zValue); - final int tGeoX = getGeoX(tx); - final int tGeoY = getGeoY(ty); - final int tz = getNearestZ(tGeoX, tGeoY, tzValue); - - if (DoorData.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return new Location(x, y, getHeight(x, y, z)); - } - if (FenceData.getInstance().checkIfFenceBetween(x, y, z, tx, ty, tz, instance)) - { - return new Location(x, y, getHeight(x, y, z)); + return false; } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = z; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return false; + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + int goz = getHeightNearest(gox, goy, oz); + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + + // Check movement within same cell. + if ((gox == gtx) && (goy == gty)) + { + return goz == getHeight(tx, ty, tz); + } + + // Get nswe flag. + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - // Can't move, return previous location. - return new Location(getWorldX(prevX), getWorldY(prevY), prevZ); - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check point heading into obstacle, if so return current point. + if ((nswe & dir) == 0) + { + return false; + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return true; // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return false; + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != tz)) - { - // Different floors, return start location. - return new Location(x, y, z); - } - - return new Location(tx, ty, tz); + // When origin Z is target Z, the move is successful. + return goz == getHeight(tx, ty, tz); } /** - * Checks if its possible to move from one location to another. - * @param fromX the X coordinate to start checking from - * @param fromY the Y coordinate to start checking from - * @param fromZvalue the Z coordinate to start checking from - * @param toX the X coordinate to end checking at - * @param toY the Y coordinate to end checking at - * @param toZvalue the Z coordinate to end checking at - * @param instance the instance - * @return {@code true} if the character at start coordinates can move to end coordinates, {@code false} otherwise + * Check movement from origin to target coordinates. Returns last available point in the checked path.
+ * Target X and Y reachable and Z is on same floor: + *
    + *
  • Location of the target with corrected Z value from geodata.
  • + *
+ * Target X and Y reachable but Z is on another floor: + *
    + *
  • Location of the origin with corrected Z value from geodata.
  • + *
+ * Target X and Y not reachable: + *
    + *
  • Last accessible location in destination to target.
  • + *
+ * @param ox : Origin X coordinate. + * @param oy : Origin Y coordinate. + * @param oz : Origin Z coordinate. + * @param tx : Target X coordinate. + * @param ty : Target Y coordinate. + * @param tz : Target Z coordinate. + * @param instance + * @return The {@link Location} representing last point of movement (e.g. just before wall). */ - public boolean canMoveToTarget(int fromX, int fromY, int fromZvalue, int toX, int toY, int toZvalue, Instance instance) + public Location getValidLocation(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) { - final int geoX = getGeoX(fromX); - final int geoY = getGeoY(fromY); - final int fromZ = getNearestZ(geoX, geoY, fromZvalue); - final int tGeoX = getGeoX(toX); - final int tGeoY = getGeoY(toY); - final int toZ = getNearestZ(tGeoX, tGeoY, toZvalue); - - if (DoorData.getInstance().checkIfDoorsBetween(fromX, fromY, fromZ, toX, toY, toZ, instance, false)) + // Door checks. + if (DoorData.getInstance().checkIfDoorsBetween(ox, oy, oz, tx, ty, tz, instance, false)) { - return false; - } - if (FenceData.getInstance().checkIfFenceBetween(fromX, fromY, fromZ, toX, toY, toZ, instance)) - { - return false; + return new Location(ox, oy, oz); } - final LinePointIterator pointIter = new LinePointIterator(geoX, geoY, tGeoX, tGeoY); - // First point is guaranteed to be available. - pointIter.next(); - int prevX = pointIter.x(); - int prevY = pointIter.y(); - int prevZ = fromZ; - - while (pointIter.next()) + // Fence checks. + if (FenceData.getInstance().checkIfFenceBetween(ox, oy, oz, tx, ty, tz, instance)) { - final int curX = pointIter.x(); - final int curY = pointIter.y(); - final int curZ = getNearestZ(curX, curY, prevZ); + return new Location(ox, oy, oz); + } + + // Get geodata coordinates. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + ABlock block = getBlock(gox, goy); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + final int gtx = getGeoX(tx); + final int gty = getGeoY(ty); + final int gtz = getHeightNearest(gtx, gty, tz); + int goz = getHeightNearest(gox, goy, oz); + int nswe = getNsweNearest(gox, goy, goz); + + // Get delta coordinates, slope of line and direction data. + final int dx = tx - ox; + final int dy = ty - oy; + final double m = (double) dy / dx; + final MoveDirectionType mdt = MoveDirectionType.getDirection(gtx - gox, gty - goy); + + // Get cell grid coordinates. + int gridX = ox & 0xFFFFFFF0; + int gridY = oy & 0xFFFFFFF0; + + // Run loop. + byte dir; + int nx = gox; + int ny = goy; + while ((gox != gtx) || (goy != gty)) + { + // Calculate intersection with cell's X border. + int checkX = gridX + mdt.getOffsetX(); + int checkY = (int) (oy + (m * (checkX - ox))); - if (hasGeoPos(prevX, prevY)) + if ((mdt.getStepX() != 0) && (getGeoY(checkY) == goy)) { - final int nswe = GeoUtils.computeNswe(prevX, prevY, curX, curY); - if (!checkNearestNsweAntiCornerCut(prevX, prevY, prevZ, nswe)) - { - return false; - } + // Set next cell is in X direction. + gridX += mdt.getStepX(); + nx += mdt.getSignumX(); + dir = mdt.getDirectionX(); + } + else + { + // Calculate intersection with cell's Y border. + checkY = gridY + mdt.getOffsetY(); + checkX = (int) (ox + ((checkY - oy) / m)); + checkX = CommonUtil.limit(checkX, gridX, gridX + 15); + + // Set next cell in Y direction. + gridY += mdt.getStepY(); + ny += mdt.getSignumY(); + dir = mdt.getDirectionY(); } - prevX = curX; - prevY = curY; - prevZ = curZ; + // Check target cell is out of geodata grid (world coordinates). + if ((nx < 0) || (nx >= GeoStructure.GEO_CELLS_X) || (ny < 0) || (ny >= GeoStructure.GEO_CELLS_Y)) + { + return new Location(checkX, checkY, goz); + } + + // Check point heading into obstacle, if so return current (border) point. + if ((nswe & dir) == 0) + { + return new Location(checkX, checkY, goz); + } + + block = getBlock(nx, ny); + if ((block == null) || !block.hasGeoPos()) + { + return new Location(tx, ty, tz); // No Geodata found. + } + + // Check next point for extensive Z difference, if so return current (border) point. + final int i = block.getIndexBelow(nx, ny, goz + GeoStructure.CELL_IGNORE_HEIGHT); + if (i < 0) + { + return new Location(checkX, checkY, goz); + } + + // Update current point's coordinates and nswe. + gox = nx; + goy = ny; + goz = block.getHeight(i); + nswe = block.getNswe(i); } - if (hasGeoPos(prevX, prevY) && (prevZ != toZ)) - { - // Different floors. - return false; - } - - return true; + // Compare Z coordinates: + // If same, path is okay, return target point and fix its Z geodata coordinate. + // If not same, path is does not exist, return origin point. + return goz == gtz ? new Location(tx, ty, gtz) : new Location(ox, oy, oz); } + /** + * Returns the list of location objects as a result of complete path calculation. + * @param ox : origin x + * @param oy : origin y + * @param oz : origin z + * @param tx : target x + * @param ty : target y + * @param tz : target z + * @param instance + * @return {@code List} : complete path from nodes + */ + public List findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance) + { + // Get origin and check existing geo coords. + int gox = getGeoX(ox); + int goy = getGeoY(oy); + if (!hasGeoPos(gox, goy)) + { + return Collections.emptyList(); + } + + int goz = getHeightNearest(gox, goy, oz); + + // Get target and check existing geo coords. + int gtx = getGeoX(tx); + int gty = getGeoY(ty); + if (!hasGeoPos(gtx, gty)) + { + return Collections.emptyList(); + } + + int gtz = getHeightNearest(gtx, gty, tz); + + // Prepare buffer for pathfinding calculations. + NodeBuffer buffer = getBuffer(300 + (10 * (Math.abs(gox - gtx) + Math.abs(goy - gty) + Math.abs(goz - gtz)))); + if (buffer == null) + { + return Collections.emptyList(); + } + + // Find path. + List path = null; + try + { + path = buffer.findPath(gox, goy, goz, gtx, gty, gtz); + if (path.isEmpty()) + { + return Collections.emptyList(); + } + } + catch (Exception e) + { + return Collections.emptyList(); + } + finally + { + buffer.free(); + } + + // Check path. + if (path.size() < 3) + { + return path; + } + + // Get path list iterator. + ListIterator point = path.listIterator(); + + // Get node A (origin). + int nodeAx = ox; + int nodeAy = oy; + int nodeAz = goz; + + // Get node B. + Location nodeB = point.next(); + + // Iterate thought the path to optimize it. + while (point.hasNext()) + { + // Get node C. + Location nodeC = path.get(point.nextIndex()); + + // Check movement from node A to node C. + if (canMoveToTarget(nodeAx, nodeAy, nodeAz, nodeC.getX(), nodeC.getY(), nodeC.getZ(), instance)) + { + // Can move from node A to node C. + // Remove node B. + point.remove(); + } + else + { + // Can not move from node A to node C. + // Set node A (node B is part of path, update A coordinates). + nodeAx = nodeB.getX(); + nodeAy = nodeB.getY(); + nodeAz = nodeB.getZ(); + } + + // Set node B. + nodeB = point.next(); + } + + return path; + } + + /** + * NodeBuffer container with specified size and count of separate buffers. + */ + private static class BufferHolder + { + final int _size; + final List _buffer; + + public BufferHolder(int size, int count) + { + _size = size; + _buffer = new ArrayList<>(count); + + for (int i = 0; i < count; i++) + { + _buffer.add(new NodeBuffer(size)); + } + } + } + + /** + * Returns the instance of the {@link GeoEngine}. + * @return {@link GeoEngine} : The instance. + */ public static GeoEngine getInstance() { return SingletonHolder.INSTANCE; @@ -737,4 +1054,4 @@ public class GeoEngine { protected static final GeoEngine INSTANCE = new GeoEngine(); } -} +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java deleted file mode 100644 index cc76b7523a..0000000000 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/GeoEnginePathfinding.java +++ /dev/null @@ -1,301 +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 . - */ -package org.l2jmobius.gameserver.geoengine; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.ListIterator; -import java.util.logging.Level; -import java.util.logging.Logger; - -import org.l2jmobius.Config; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNode; -import org.l2jmobius.gameserver.geoengine.pathfinding.CellNodeBuffer; -import org.l2jmobius.gameserver.geoengine.pathfinding.NodeLoc; -import org.l2jmobius.gameserver.model.World; -import org.l2jmobius.gameserver.model.instancezone.Instance; - -/** - * @author -Nemesiss- - */ -public class GeoEnginePathfinding -{ - private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName()); - - private BufferInfo[] _buffers; - - protected GeoEnginePathfinding() - { - try - { - final String[] array = Config.PATHFIND_BUFFERS.split(";"); - - _buffers = 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); - } - - _buffers[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"); - } - } - - public boolean pathNodesExist(short regionoffset) - { - return false; - } - - public List findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance) - { - 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)))); - if (buffer == null) - { - return null; - } - - List path = null; - try - { - final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz); - - if (result == null) - { - return null; - } - - path = constructPath(result); - } - catch (Exception e) - { - LOGGER.warning(e.getMessage()); - return null; - } - finally - { - buffer.free(); - } - - // check path - if (path.size() < 3) - { - return path; - } - - int currentX, currentY, currentZ; - ListIterator middlePoint; - - middlePoint = path.listIterator(); - currentX = x; - currentY = y; - currentZ = z; - - while (middlePoint.hasNext()) - { - final AbstractNodeLoc locMiddle = middlePoint.next(); - if (!middlePoint.hasNext()) - { - break; - } - - final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex()); - if (GeoEngine.getInstance().canMoveToTarget(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance)) - { - middlePoint.remove(); - } - else - { - currentX = locMiddle.getX(); - currentY = locMiddle.getY(); - currentZ = locMiddle.getZ(); - } - } - - return path; - } - - /** - * Convert geodata position to pathnode position - * @param geo_pos - * @return pathnode position - */ - public short getNodePos(int geo_pos) - { - return (short) (geo_pos >> 3); // OK? - } - - /** - * Convert node position to pathnode block position - * @param node_pos - * @return pathnode block position (0...255) - */ - public short getNodeBlock(int node_pos) - { - return (short) (node_pos % 256); - } - - public byte getRegionX(int node_pos) - { - return (byte) ((node_pos >> 8) + World.TILE_X_MIN); - } - - public byte getRegionY(int node_pos) - { - return (byte) ((node_pos >> 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 node_x rx - * @return - */ - public int calculateWorldX(short node_x) - { - return World.MAP_MIN_X + (node_x * 128) + 48; - } - - /** - * Convert pathnode y to World y position - * @param node_y - * @return - */ - public int calculateWorldY(short node_y) - { - return World.MAP_MIN_Y + (node_y * 128) + 48; - } - - private List constructPath(AbstractNode nodeValue) - { - final LinkedList path = new LinkedList<>(); - int previousDirectionX = Integer.MIN_VALUE; - int previousDirectionY = Integer.MIN_VALUE; - int directionX, directionY; - - AbstractNode node = nodeValue; - while (node.getParent() != null) - { - directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); - directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); - - // only add a new route point if moving direction changes - if ((directionX != previousDirectionX) || (directionY != previousDirectionY)) - { - previousDirectionX = directionX; - previousDirectionY = directionY; - - path.addFirst(node.getLoc()); - node.setLoc(null); - } - - node = node.getParent(); - } - - return path; - } - - private CellNodeBuffer alloc(int size) - { - CellNodeBuffer current = null; - for (BufferInfo i : _buffers) - { - if (i.mapSize >= size) - { - for (CellNodeBuffer buf : i.buffer) - { - if (buf.lock()) - { - current = buf; - break; - } - } - if (current != null) - { - break; - } - - // not found, allocate temporary buffer - current = new CellNodeBuffer(i.mapSize); - current.lock(); - if (i.buffer.size() < i.count) - { - i.buffer.add(current); - break; - } - } - } - - return current; - } - - private static final class BufferInfo - { - final int mapSize; - final int count; - ArrayList buffer; - - public BufferInfo(int size, int cnt) - { - mapSize = size; - count = cnt; - buffer = new ArrayList<>(count); - } - } - - public static GeoEnginePathfinding getInstance() - { - return SingletonHolder.INSTANCE; - } - - private static class SingletonHolder - { - protected static final GeoEnginePathfinding INSTANCE = new GeoEnginePathfinding(); - } -} diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java new file mode 100644 index 0000000000..49ec35aa1c --- /dev/null +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/ABlock.java @@ -0,0 +1,85 @@ +/* + * 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 . + */ +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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @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.
+ * @param index : Index of the cell. + * @return short : Cell geodata Z coordinate, below given coordinates. + */ + public abstract byte getNswe(int index); +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java new file mode 100644 index 0000000000..f5997bf287 --- /dev/null +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/BlockComplex.java @@ -0,0 +1,130 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java new file mode 100644 index 0000000000..9af9d2a28a --- /dev/null +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/BlockFlat.java @@ -0,0 +1,95 @@ +/* + * 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 . + */ +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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java new file mode 100644 index 0000000000..31fbf5d477 --- /dev/null +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/BlockMultilayer.java @@ -0,0 +1,248 @@ +/* + * 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 . + */ +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]; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java new file mode 100644 index 0000000000..f77d21d84b --- /dev/null +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/BlockNull.java @@ -0,0 +1,68 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.geodata; + +public class BlockNull extends ABlock +{ + @Override + public boolean hasGeoPos() + { + 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; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java deleted file mode 100644 index 61ea4fcf82..0000000000 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/Cell.java +++ /dev/null @@ -1,48 +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 . - */ -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() - { - } -} diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java deleted file mode 100644 index 3c8de1a7f7..0000000000 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/ComplexBlock.java +++ /dev/null @@ -1,77 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java deleted file mode 100644 index 1ccdefe3da..0000000000 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/FlatBlock.java +++ /dev/null @@ -1,56 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java new file mode 100644 index 0000000000..691b164e0c --- /dev/null +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/GeoStructure.java @@ -0,0 +1,65 @@ +/* + * 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 . + */ +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_ALL = 0x0F; + + // 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; +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java deleted file mode 100644 index 25eafc5a04..0000000000 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/IBlock.java +++ /dev/null @@ -1,42 +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 . - */ -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); -} diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java deleted file mode 100644 index 0d2c765002..0000000000 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/IRegion.java +++ /dev/null @@ -1,47 +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 . - */ -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(); -} diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java deleted file mode 100644 index 52df457360..0000000000 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/MultilayerBlock.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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); - - // 1 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; - } -} diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java deleted file mode 100644 index 72a5a635c7..0000000000 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/NullRegion.java +++ /dev/null @@ -1,55 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -/** - * @author HorridoJoho - */ -public final class NullRegion implements IRegion -{ - public static final NullRegion INSTANCE = new NullRegion(); - - @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 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; - } -} diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java deleted file mode 100644 index fc924cb875..0000000000 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/geodata/Region.java +++ /dev/null @@ -1,92 +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 . - */ -package org.l2jmobius.gameserver.geoengine.geodata; - -import java.nio.ByteBuffer; - -/** - * @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; - } -} diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java deleted file mode 100644 index 5087513ed9..0000000000 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNode.java +++ /dev/null @@ -1,87 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public abstract class AbstractNode -{ - private T _loc; - private AbstractNode _parent; - - public AbstractNode(T loc) - { - _loc = loc; - } - - public void setParent(AbstractNode p) - { - _parent = p; - } - - public AbstractNode getParent() - { - return _parent; - } - - public T getLoc() - { - return _loc; - } - - public void setLoc(T l) - { - _loc = l; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 1; - result = (prime * result) + ((_loc == null) ? 0 : _loc.hashCode()); - return result; - } - - @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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java deleted file mode 100644 index 7223b5b2be..0000000000 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/AbstractNodeLoc.java +++ /dev/null @@ -1,33 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -/** - * @author -Nemesiss- - */ -public abstract class AbstractNodeLoc -{ - public abstract int getX(); - - public abstract int getY(); - - public abstract int getZ(); - - public abstract int getNodeX(); - - public abstract int getNodeY(); -} diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java deleted file mode 100644 index 3425234e0e..0000000000 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNode.java +++ /dev/null @@ -1,67 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -public class CellNode extends AbstractNode -{ - 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java deleted file mode 100644 index 522610bb7c..0000000000 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/CellNodeBuffer.java +++ /dev/null @@ -1,348 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -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 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) - { - _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(); - } - - public final List debugPath() - { - final List 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 void getNeighbors() - { - if (!_current.getLoc().canGoAll()) - { - 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); - } - - // SouthEast - if ((nodeE != null) && (nodeS != null)) - { - if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast()) - { - addNode(x + 1, y + 1, z, true); - } - } - - // SouthWest - if ((nodeS != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest()) - { - addNode(x - 1, y + 1, z, true); - } - } - - // NorthEast - if ((nodeN != null) && (nodeE != null)) - { - if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast()) - { - addNode(x + 1, y - 1, z, true); - } - } - - // NorthWest - if ((nodeN != null) && (nodeW != null)) - { - if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest()) - { - addNode(x - 1, y - 1, z, true); - } - } - } - - private 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(); - // reinit node if needed - if (result.getLoc() != null) - { - result.getLoc().set(x, y, z); - } - else - { - result.setLoc(new NodeLoc(x, y, z)); - } - } - - return result; - } - - private 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 boolean isHighWeight(int x, int y, int z) - { - final CellNode result = getNode(x, y, z); - if (result == null) - { - return true; - } - - if (!result.getLoc().canGoAll()) - { - return true; - } - if (Math.abs(result.getLoc().getZ() - z) > 16) - { - return true; - } - - return false; - } - - private 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; - } -} \ No newline at end of file diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java new file mode 100644 index 0000000000..fa5ca43c2f --- /dev/null +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/Node.java @@ -0,0 +1,111 @@ +/* + * 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 . + */ +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 geodata values. + private int _geoX; + private int _geoY; + private byte _nswe; + + // 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; + + _costG = 0; + _costH = 0; + _costF = 0; + + _parent = null; + } + + public void setGeo(int gx, int gy, int gz, byte nswe) + { + super.setXYZ(GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), gz); + + _geoX = gx; + _geoY = gy; + _nswe = nswe; + } + + 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 int getCostF() + { + return _costF; + } + + public Node getParent() + { + return _parent; + } + + @Override + public int compareTo(Node o) + { + return _costF - o._costF; + } +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java new file mode 100644 index 0000000000..b464212c96 --- /dev/null +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeBuffer.java @@ -0,0 +1,368 @@ +/* + * 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 . + */ +package org.l2jmobius.gameserver.geoengine.pathfinding; + +import java.awt.Color; +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; +import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; + +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 _opened; + // Container holding Nodes already explored. + private final List _closed; + + // Target coordinates. + private int _gtx; + private int _gty; + private int _gtz; + + // Pathfinding statistics. + private long _timeStamp; + private long _lastElapsedTime; + + 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 findPath(int gox, int goy, int goz, int gtx, int gty, int gtz) + { + // Set start timestamp. + _timeStamp = System.currentTimeMillis(); + + // 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)); + _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 constructPath() + { + // Create result. + final LinkedList 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; + } + + /** + * Creates list of Nodes to show debug path. + * @param debug : The debug packet to add debug informations in. + */ + public void debugPath(ExServerPrimitive debug) + { + // Add all opened node as yellow points. + for (Node n : _opened) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.YELLOW, true, n.getX(), n.getY(), n.getZ() - 16); + } + + // Add all opened node as blue points. + for (Node n : _closed) + { + debug.addPoint(String.valueOf(n.getCostF()), Color.BLUE, true, n.getX(), n.getY(), n.getZ() - 16); + } + } + + 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; + + _lastElapsedTime = System.currentTimeMillis() - _timeStamp; + _lock.unlock(); + } + + public long getElapsedTime() + { + return _lastElapsedTime; + } + + /** + * Expand the current {@link Node} by exploring its neighbors (axially and diagonally). + */ + private void expand() + { + // Movement is blocked, skip. + final byte nswe = _current.getNSWE(); + if (nswe == 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; + + // Can move north, expand. + if ((nswe & GeoStructure.CELL_FLAG_N) != 0) + { + nsweN = addNode(x, y - 1, z, Config.MOVE_WEIGHT); + } + + // Can move south, expand. + if ((nswe & GeoStructure.CELL_FLAG_S) != 0) + { + nsweS = addNode(x, y + 1, z, Config.MOVE_WEIGHT); + } + + // Can move west, expand. + if ((nswe & GeoStructure.CELL_FLAG_W) != 0) + { + nsweW = addNode(x - 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move east, expand. + if ((nswe & GeoStructure.CELL_FLAG_E) != 0) + { + nsweE = addNode(x + 1, y, z, Config.MOVE_WEIGHT); + } + + // Can move north-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move north-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_N) != 0) && ((nsweN & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y - 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-west, expand. + if (((nsweW & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_W) != 0)) + { + addNode(x - 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + + // Can move south-east, expand. + if (((nsweE & GeoStructure.CELL_FLAG_S) != 0) && ((nsweS & GeoStructure.CELL_FLAG_E) != 0)) + { + addNode(x + 1, y + 1, z, Config.MOVE_WEIGHT_DIAG); + } + } + + /** + * 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 weight : The weight of movement to the new node. + * @return The nswe of the added node. Blank, if not added. + */ + private byte addNode(int gx, int gy, int gzValue, int weight) + { + // 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]; + + // Set node geodata coordinates. + node.setGeo(gx, gy, gz, nswe); + + // 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, nswe != GeoStructure.CELL_FLAG_ALL ? Config.OBSTACLE_WEIGHT : weight, getCostH(gx, gy, gz)); + _opened.add(node); + _bufferIndex++; + return nswe; + } + + /** + * Calculate cost H value, calculated using diagonal distance method.
+ * 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. + final int dx = Math.abs(gx - _gtx); + final int dy = Math.abs(gy - _gty); + final int dz = Math.abs(gz - _gtz) / GeoStructure.CELL_HEIGHT; + + // Get diagonal and axial differences to the target. + final int dd = Math.min(dx, dy); + final int da = Math.max(dx, dy) - dd; + + // Calculate the diagonal distance of the node to the target. + return (dd * Config.HEURISTIC_WEIGHT_DIAG) + ((da + dz) * Config.HEURISTIC_WEIGHT); + } +} \ No newline at end of file diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java deleted file mode 100644 index e3bad04b25..0000000000 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/geoengine/pathfinding/NodeLoc.java +++ /dev/null @@ -1,183 +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 . - */ -package org.l2jmobius.gameserver.geoengine.pathfinding; - -import org.l2jmobius.gameserver.geoengine.GeoEngine; -import org.l2jmobius.gameserver.geoengine.geodata.Cell; - -/** - * @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 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 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; - // return super.hashCode(); - } - - @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; - if (_x != other._x) - { - return false; - } - if (_y != other._y) - { - return false; - } - if (_goNorth != other._goNorth) - { - return false; - } - if (_goEast != other._goEast) - { - return false; - } - if (_goSouth != other._goSouth) - { - return false; - } - if (_goWest != other._goWest) - { - return false; - } - if (_geoHeight != other._geoHeight) - { - return false; - } - return true; - } -} diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java index fec7e24f5e..c93649d8fd 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/instancemanager/ZoneManager.java @@ -94,15 +94,15 @@ public class ZoneManager implements IXmlReader private static final Map SETTINGS = new HashMap<>(); private static final int SHIFT_BY = 15; - private static final int OFFSET_X = Math.abs(World.MAP_MIN_X >> SHIFT_BY); - private static final int OFFSET_Y = Math.abs(World.MAP_MIN_Y >> SHIFT_BY); + private static final int OFFSET_X = Math.abs(World.WORLD_X_MIN >> SHIFT_BY); + private static final int OFFSET_Y = Math.abs(World.WORLD_Y_MIN >> SHIFT_BY); private final Map, ConcurrentHashMap> _classZones = new ConcurrentHashMap<>(); private final Map _spawnTerritories = new ConcurrentHashMap<>(); private final AtomicInteger _lastDynamicId = new AtomicInteger(300000); private List _debugItems; - private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.MAP_MAX_X >> SHIFT_BY) + OFFSET_X + 1][(World.MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y + 1]; + private final ZoneRegion[][] _zoneRegions = new ZoneRegion[(World.WORLD_X_MAX >> SHIFT_BY) + OFFSET_X + 1][(World.WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y + 1]; /** * Instantiates a new zone manager. diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/Location.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/Location.java index 73689ab1a6..ca3b22a3f0 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/Location.java +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/Location.java @@ -16,30 +16,30 @@ */ package org.l2jmobius.gameserver.model; +import java.util.Objects; + +import org.l2jmobius.commons.util.Point2D; import org.l2jmobius.gameserver.model.interfaces.ILocational; import org.l2jmobius.gameserver.model.interfaces.IPositionable; /** - * Location data transfer object.
- * Contains coordinates data, heading and instance Id. - * @author Zoey76 + * A datatype used to retain a 3D (x/y/z/heading) point. It got the capability to be set and cleaned. */ -public class Location implements IPositionable +public class Location extends Point2D implements IPositionable { - protected int _x; - protected int _y; - protected int _z; - private int _heading; + protected volatile int _z; + protected volatile int _heading; public Location(int x, int y, int z) { - this(x, y, z, 0); + super(x, y); + _z = z; + _heading = 0; } public Location(int x, int y, int z, int heading) { - _x = x; - _y = y; + super(x, y); _z = z; _heading = heading; } @@ -51,9 +51,8 @@ public class Location implements IPositionable public Location(StatSet set) { - _x = set.getInt("x"); - _y = set.getInt("y"); - _z = set.getInt("z"); + super(set.getInt("x", 0), set.getInt("y", 0)); + _z = set.getInt("z", 0); _heading = set.getInt("heading", 0); } @@ -146,6 +145,25 @@ public class Location implements IPositionable _heading = loc.getHeading(); } + @Override + public void clean() + { + super.clean(); + _z = 0; + } + + @Override + public Location clone() + { + return new Location(_x, _y, _z); + } + + @Override + public int hashCode() + { + return (31 * super.hashCode()) + Objects.hash(_z); + } + @Override public boolean equals(Object obj) { diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/World.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/World.java index c1510e8c0e..0d796dd834 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/World.java +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/World.java @@ -66,19 +66,19 @@ public class World public static final int TILE_Y_MAX = 26; public static final int TILE_ZERO_COORD_X = 20; public static final int TILE_ZERO_COORD_Y = 18; - public static final int MAP_MIN_X = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; - public static final int MAP_MIN_Y = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; + public static final int WORLD_X_MIN = (TILE_X_MIN - TILE_ZERO_COORD_X) * TILE_SIZE; + public static final int WORLD_Y_MIN = (TILE_Y_MIN - TILE_ZERO_COORD_Y) * TILE_SIZE; - public static final int MAP_MAX_X = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; - public static final int MAP_MAX_Y = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; + public static final int WORLD_X_MAX = ((TILE_X_MAX - TILE_ZERO_COORD_X) + 1) * TILE_SIZE; + public static final int WORLD_Y_MAX = ((TILE_Y_MAX - TILE_ZERO_COORD_Y) + 1) * TILE_SIZE; /** Calculated offset used so top left region is 0,0 */ - public static final int OFFSET_X = Math.abs(MAP_MIN_X >> SHIFT_BY); - public static final int OFFSET_Y = Math.abs(MAP_MIN_Y >> SHIFT_BY); + public static final int OFFSET_X = Math.abs(WORLD_X_MIN >> SHIFT_BY); + public static final int OFFSET_Y = Math.abs(WORLD_Y_MIN >> SHIFT_BY); /** Number of regions. */ - private static final int REGIONS_X = (MAP_MAX_X >> SHIFT_BY) + OFFSET_X; - private static final int REGIONS_Y = (MAP_MAX_Y >> SHIFT_BY) + OFFSET_Y; + private static final int REGIONS_X = (WORLD_X_MAX >> SHIFT_BY) + OFFSET_X; + private static final int REGIONS_Y = (WORLD_Y_MAX >> SHIFT_BY) + OFFSET_Y; /** Map containing all the players in game. */ private static final Map _allPlayers = new ConcurrentHashMap<>(); @@ -817,9 +817,6 @@ public class World return _memberInPartyNumber.get(); } - /** - * @return the current instance of World - */ public static World getInstance() { return SingletonHolder.INSTANCE; diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/WorldObject.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/WorldObject.java index 0e5536bbc0..62313ffea1 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/WorldObject.java +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/WorldObject.java @@ -188,23 +188,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif synchronized (this) { int spawnX = x; - if (spawnX > World.MAP_MAX_X) + if (spawnX > World.WORLD_X_MAX) { - spawnX = World.MAP_MAX_X - 5000; + spawnX = World.WORLD_X_MAX - 5000; } - if (spawnX < World.MAP_MIN_X) + if (spawnX < World.WORLD_X_MIN) { - spawnX = World.MAP_MIN_X + 5000; + spawnX = World.WORLD_X_MIN + 5000; } int spawnY = y; - if (spawnY > World.MAP_MAX_Y) + if (spawnY > World.WORLD_Y_MAX) { - spawnY = World.MAP_MAX_Y - 5000; + spawnY = World.WORLD_Y_MAX - 5000; } - if (spawnY < World.MAP_MIN_Y) + if (spawnY < World.WORLD_Y_MIN) { - spawnY = World.MAP_MIN_Y + 5000; + spawnY = World.WORLD_Y_MIN + 5000; } // Set the x,y,z position of the WorldObject. If flagged with _isSpawned, setXYZ will automatically update world region, so avoid that. @@ -518,23 +518,23 @@ public abstract class WorldObject extends ListenersContainer implements IIdentif public void setXYZInvisible(int x, int y, int z) { int correctX = x; - if (correctX > World.MAP_MAX_X) + if (correctX > World.WORLD_X_MAX) { - correctX = World.MAP_MAX_X - 5000; + correctX = World.WORLD_X_MAX - 5000; } - if (correctX < World.MAP_MIN_X) + if (correctX < World.WORLD_X_MIN) { - correctX = World.MAP_MIN_X + 5000; + correctX = World.WORLD_X_MIN + 5000; } int correctY = y; - if (correctY > World.MAP_MAX_Y) + if (correctY > World.WORLD_Y_MAX) { - correctY = World.MAP_MAX_Y - 5000; + correctY = World.WORLD_Y_MAX - 5000; } - if (correctY < World.MAP_MIN_Y) + if (correctY < World.WORLD_Y_MIN) { - correctY = World.MAP_MIN_Y + 5000; + correctY = World.WORLD_Y_MIN + 5000; } setXYZ(correctX, correctY, z); diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/actor/Creature.java index f52c2aa32b..bdf210f78a 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/actor/Creature.java @@ -66,8 +66,6 @@ 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.GeoEnginePathfinding; -import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc; import org.l2jmobius.gameserver.instancemanager.IdManager; import org.l2jmobius.gameserver.instancemanager.MapRegionManager; import org.l2jmobius.gameserver.instancemanager.QuestManager; @@ -2596,7 +2594,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe public boolean disregardingGeodata; public int onGeodataPathIndex; - public List geoPath; + public List geoPath; public int geoPathAccurateTx; public int geoPathAccurateTy; public int geoPathGtx; @@ -3428,8 +3426,8 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe final int originalX = x; final int originalY = y; final int originalZ = z; - final int gtx = (originalX - World.MAP_MIN_X) >> 4; - final int gty = (originalY - World.MAP_MIN_Y) >> 4; + final int gtx = (originalX - World.WORLD_X_MIN) >> 4; + final int gty = (originalY - World.WORLD_Y_MIN) >> 4; if (isOnGeodataPath()) { try @@ -3452,7 +3450,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe && !(((curZ - z) > 300) && (distance < 300))) // Prohibit correcting destination if character wants to fall. { // location different if destination wasn't reached (or just z coord is different) - final Location destiny = GeoEngine.getInstance().canMoveToTargetLoc(curX, curY, curZ, x, y, z, getInstanceWorld()); + final Location destiny = GeoEngine.getInstance().getValidLocation(curX, curY, curZ, x, y, z, getInstanceWorld()); x = destiny.getX(); y = destiny.getY(); z = destiny.getZ(); @@ -3466,7 +3464,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle) { // Path calculation -- overrides previous movement check - m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); + m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld()); if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found { if (isPlayer() && !_isFlying && !isInWater) diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/actor/Summon.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/actor/Summon.java index 386239d549..6ed43c885f 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/actor/Summon.java +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/actor/Summon.java @@ -106,7 +106,7 @@ public abstract class Summon extends Playable final int x = owner.getX(); final int y = owner.getY(); final int z = owner.getZ(); - final Location location = GeoEngine.getInstance().canMoveToTargetLoc(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, owner.getInstanceWorld()); + final Location location = GeoEngine.getInstance().getValidLocation(x, y, z, x + Rnd.get(-100, 100), y + Rnd.get(-100, 100), z, getInstanceWorld()); setXYZInvisible(location.getX(), location.getY(), location.getZ()); } diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java index d1d33cdca6..62536d78a1 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/actor/instance/PlayerInstance.java @@ -14385,8 +14385,8 @@ public class PlayerInstance extends Playable public boolean isInTimedHuntingZone(int zoneId, int locX, int locY) { - final int x = ((locX - World.MAP_MIN_X) >> 15) + World.TILE_X_MIN; - final int y = ((locY - World.MAP_MIN_Y) >> 15) + World.TILE_Y_MIN; + final int x = ((locX - World.WORLD_X_MIN) >> 15) + World.TILE_X_MIN; + final int y = ((locY - World.WORLD_Y_MIN) >> 15) + World.TILE_Y_MIN; switch (zoneId) { diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java index abaec9036f..20cd1966d5 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/items/instance/ItemInstance.java @@ -1560,7 +1560,7 @@ public class ItemInstance extends WorldObject if (dropper != null) { final Instance instance = dropper.getInstanceWorld(); - final Location dropDest = GeoEngine.getInstance().canMoveToTargetLoc(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); + final Location dropDest = GeoEngine.getInstance().getValidLocation(dropper.getX(), dropper.getY(), dropper.getZ(), x, y, z, instance); x = dropDest.getX(); y = dropDest.getY(); z = dropDest.getZ(); diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java index 44db75b858..02113758c2 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/model/skills/SkillCaster.java @@ -1184,7 +1184,7 @@ public class SkillCaster implements Runnable } } - final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().canMoveToTargetLoc(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); + final Location destination = creature.isFlying() ? new Location(x, y, z) : GeoEngine.getInstance().getValidLocation(creature.getX(), creature.getY(), creature.getZ(), x, y, z, creature.getInstanceWorld()); creature.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); creature.broadcastPacket(new FlyToLocation(creature, destination, flyType, 0, 0, 333)); diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java index c62c821f44..30068134e3 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/network/clientpackets/ValidatePosition.java @@ -114,7 +114,7 @@ public class ValidatePosition implements IClientIncomingPacket { if (player.isFalling(_z)) { - final int nearestZ = GeoEngine.getInstance().getHigherHeight(_x, _y, _z); + final int nearestZ = GeoEngine.getInstance().getHeight(_x, _y, _z); if (player.getZ() < nearestZ) { player.setXYZ(_x, _y, nearestZ); diff --git a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/util/GeoUtils.java b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/util/GeoUtils.java index 4600a0fc53..19a979819e 100644 --- a/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/util/GeoUtils.java +++ b/L2J_Mobius_Essence_5.0_Sylph/java/org/l2jmobius/gameserver/util/GeoUtils.java @@ -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.Cell; +import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure; import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance; import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; @@ -30,21 +30,21 @@ public final class GeoUtils { public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z); final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy); while (iter.next()) { - final int wx = GeoEngine.getInstance().getWorldX(iter.x()); - final int wy = GeoEngine.getInstance().getWorldY(iter.y()); + final int wx = GeoEngine.getWorldX(iter.x()); + final int wy = GeoEngine.getWorldY(iter.y()); prim.addPoint(Color.RED, wx, wy, z); } @@ -53,21 +53,21 @@ public final class GeoUtils public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz) { - final int gx = GeoEngine.getInstance().getGeoX(x); - final int gy = GeoEngine.getInstance().getGeoY(y); + final int gx = GeoEngine.getGeoX(x); + final int gy = GeoEngine.getGeoY(y); - final int tgx = GeoEngine.getInstance().getGeoX(tx); - final int tgy = GeoEngine.getInstance().getGeoY(ty); + final int tgx = GeoEngine.getGeoX(tx); + final int tgy = GeoEngine.getGeoY(ty); final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z); - prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz); + prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.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.getInstance().getWorldX(prevX); - int wy = GeoEngine.getInstance().getWorldY(prevY); + int wx = GeoEngine.getWorldX(prevX); + int wy = GeoEngine.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.getInstance().getWorldX(curX); - wy = GeoEngine.getInstance().getWorldY(curY); + wx = GeoEngine.getWorldX(curX); + wy = GeoEngine.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().checkNearestNswe(x, y, z, nswe)) + if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) != 0) { return Color.GREEN; } @@ -109,9 +109,8 @@ public final class GeoUtils int iPacket = 0; ExServerPrimitive exsp = null; - final GeoEngine ge = GeoEngine.getInstance(); - final int playerGx = ge.getGeoX(player.getX()); - final int playerGy = ge.getGeoY(player.getY()); + final int playerGx = GeoEngine.getGeoX(player.getX()); + final int playerGy = GeoEngine.getGeoY(player.getY()); for (int dx = -geoRadius; dx <= geoRadius; ++dx) { for (int dy = -geoRadius; dy <= geoRadius; ++dy) @@ -135,32 +134,32 @@ public final class GeoUtils final int gx = playerGx + dx; final int gy = playerGy + dy; - final int x = ge.getWorldX(gx); - final int y = ge.getWorldY(gy); - final int z = ge.getNearestZ(gx, gy, player.getZ()); + final int x = GeoEngine.getWorldX(gx); + final int y = GeoEngine.getWorldY(gy); + final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ()); // north arrow - Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH); + Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N); 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, Cell.NSWE_EAST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E); 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, Cell.NSWE_SOUTH); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S); 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, Cell.NSWE_WEST); + col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W); 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); @@ -188,41 +187,41 @@ public final class GeoUtils { if (y > lastY) { - return Cell.NSWE_SOUTH_EAST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_E; } else if (y < lastY) { - return Cell.NSWE_NORTH_EAST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_E; } else { - return Cell.NSWE_EAST; + return GeoStructure.CELL_FLAG_E; } } else if (x < lastX) // west { if (y > lastY) { - return Cell.NSWE_SOUTH_WEST; + return GeoStructure.CELL_FLAG_S & GeoStructure.CELL_FLAG_W; } else if (y < lastY) { - return Cell.NSWE_NORTH_WEST; + return GeoStructure.CELL_FLAG_N & GeoStructure.CELL_FLAG_W; } else { - return Cell.NSWE_WEST; + return GeoStructure.CELL_FLAG_W; } } else // unchanged x { if (y > lastY) { - return Cell.NSWE_SOUTH; + return GeoStructure.CELL_FLAG_S; } else if (y < lastY) { - return Cell.NSWE_NORTH; + return GeoStructure.CELL_FLAG_N; } else {