Reverted back to l2j geoengine.
This commit is contained in:
@ -32,6 +32,7 @@ import java.net.URL;
|
||||
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;
|
||||
@ -939,17 +940,14 @@ public class Config
|
||||
// --------------------------------------------------
|
||||
// GeoEngine
|
||||
// --------------------------------------------------
|
||||
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 Path GEODATA_PATH;
|
||||
public static boolean PATHFINDING;
|
||||
public static String PATHFIND_BUFFERS;
|
||||
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 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 boolean CORRECT_PLAYER_Z;
|
||||
|
||||
/** Attribute System */
|
||||
@ -2492,17 +2490,14 @@ public class Config
|
||||
|
||||
// Load GeoEngine config file (if exists)
|
||||
final PropertiesParser GeoEngine = new PropertiesParser(GEOENGINE_CONFIG_FILE);
|
||||
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);
|
||||
GEODATA_PATH = Paths.get(GeoEngine.getString("GeoDataPath", "./data/geodata"));
|
||||
PATHFINDING = GeoEngine.getBoolean("PathFinding", true);
|
||||
PATHFIND_BUFFERS = GeoEngine.getString("PathFindBuffers", "100x6;128x6;192x6;256x4;320x4;384x4;500x2");
|
||||
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);
|
||||
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);
|
||||
CORRECT_PLAYER_Z = GeoEngine.getBoolean("CorrectPlayerZ", false);
|
||||
|
||||
// Load AllowedPlayerRaces config file (if exists)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -20,84 +20,88 @@ 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.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.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 Hasha
|
||||
* @author -Nemesiss-
|
||||
*/
|
||||
final class GeoEnginePathfinding extends GeoEngine
|
||||
public class GeoEnginePathfinding
|
||||
{
|
||||
// pre-allocated buffers
|
||||
private final BufferHolder[] _buffers;
|
||||
private static final Logger LOGGER = Logger.getLogger(GeoEnginePathfinding.class.getName());
|
||||
|
||||
private BufferInfo[] _buffers;
|
||||
|
||||
protected GeoEnginePathfinding()
|
||||
{
|
||||
super();
|
||||
|
||||
final String[] array = Config.PATHFIND_BUFFERS.split(";");
|
||||
_buffers = new BufferHolder[array.length];
|
||||
int count = 0;
|
||||
for (int i = 0; i < array.length; i++)
|
||||
try
|
||||
{
|
||||
final String buf = array[i];
|
||||
final String[] args = buf.split("x");
|
||||
final String[] array = Config.PATHFIND_BUFFERS.split(";");
|
||||
|
||||
try
|
||||
_buffers = new BufferInfo[array.length];
|
||||
|
||||
String buf;
|
||||
String[] args;
|
||||
for (int i = 0; i < array.length; i++)
|
||||
{
|
||||
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);
|
||||
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]));
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.info("GeoEnginePathfinding: Loaded " + count + " node buffers.");
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e);
|
||||
throw new Error("CellPathFinding: load aborted");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Location> findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance)
|
||||
public boolean pathNodesExist(short regionoffset)
|
||||
{
|
||||
// get origin and check existing geo coords
|
||||
final int gox = getGeoX(ox);
|
||||
final int goy = getGeoY(oy);
|
||||
if (!hasGeoPos(gox, goy))
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<AbstractNodeLoc> 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 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))
|
||||
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 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))));
|
||||
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;
|
||||
}
|
||||
|
||||
// find path
|
||||
List<Location> path = null;
|
||||
List<AbstractNodeLoc> path = null;
|
||||
try
|
||||
{
|
||||
final Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz);
|
||||
final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz);
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
return null;
|
||||
@ -121,147 +125,177 @@ final class GeoEnginePathfinding extends GeoEngine
|
||||
return path;
|
||||
}
|
||||
|
||||
// get path list iterator
|
||||
final ListIterator<Location> point = path.listIterator();
|
||||
int currentX, currentY, currentZ;
|
||||
ListIterator<AbstractNodeLoc> middlePoint;
|
||||
|
||||
// get node A (origin)
|
||||
int nodeAx = gox;
|
||||
int nodeAy = goy;
|
||||
short nodeAz = goz;
|
||||
middlePoint = path.listIterator();
|
||||
currentX = x;
|
||||
currentY = y;
|
||||
currentZ = z;
|
||||
|
||||
// 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))
|
||||
while (middlePoint.hasNext())
|
||||
{
|
||||
// get node C
|
||||
final GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex());
|
||||
|
||||
// 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()))
|
||||
final AbstractNodeLoc locMiddle = middlePoint.next();
|
||||
if (!middlePoint.hasNext())
|
||||
{
|
||||
// can move from node A to node C
|
||||
|
||||
// remove node B
|
||||
point.remove();
|
||||
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
|
||||
{
|
||||
// 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();
|
||||
currentX = locMiddle.getX();
|
||||
currentY = locMiddle.getY();
|
||||
currentZ = locMiddle.getZ();
|
||||
}
|
||||
|
||||
// set node B
|
||||
nodeB = (GeoLocation) point.next();
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create list of node locations as result of calculated buffer node tree.
|
||||
* @param node : the entry point
|
||||
* @return List<NodeLoc> : list of node location
|
||||
* Convert geodata position to pathnode position
|
||||
* @param geo_pos
|
||||
* @return pathnode position
|
||||
*/
|
||||
private static List<Location> constructPath(Node node)
|
||||
public short getNodePos(int geo_pos)
|
||||
{
|
||||
// create empty list
|
||||
final LinkedList<Location> list = new LinkedList<>();
|
||||
|
||||
// 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))
|
||||
{
|
||||
// get parent <> target direction X/Y
|
||||
final int nx = parent.getLoc().getGeoX() - target.getLoc().getGeoX();
|
||||
final int ny = parent.getLoc().getGeoY() - target.getLoc().getGeoY();
|
||||
|
||||
// direction has changed?
|
||||
if ((dx != nx) || (dy != ny))
|
||||
{
|
||||
// add node to the beginning of the list
|
||||
list.addFirst(target.getLoc());
|
||||
|
||||
// update direction X/Y
|
||||
dx = nx;
|
||||
dy = ny;
|
||||
}
|
||||
|
||||
// move to next node, set target and get its parent
|
||||
target = parent;
|
||||
parent = target.getParent();
|
||||
}
|
||||
|
||||
// return list
|
||||
return list;
|
||||
return (short) (geo_pos >> 3); // OK?
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* Convert node position to pathnode block position
|
||||
* @param node_pos
|
||||
* @return pathnode block position (0...255)
|
||||
*/
|
||||
private final NodeBuffer getBuffer(int size)
|
||||
public short getNodeBlock(int node_pos)
|
||||
{
|
||||
NodeBuffer current = null;
|
||||
for (BufferHolder holder : _buffers)
|
||||
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<AbstractNodeLoc> constructPath(AbstractNode<NodeLoc> nodeValue)
|
||||
{
|
||||
final LinkedList<AbstractNodeLoc> path = new LinkedList<>();
|
||||
int previousDirectionX = Integer.MIN_VALUE;
|
||||
int previousDirectionY = Integer.MIN_VALUE;
|
||||
int directionX, directionY;
|
||||
|
||||
AbstractNode<NodeLoc> node = nodeValue;
|
||||
while (node.getParent() != null)
|
||||
{
|
||||
// Find proper size of buffer
|
||||
if (holder._size < size)
|
||||
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))
|
||||
{
|
||||
continue;
|
||||
previousDirectionX = directionX;
|
||||
previousDirectionY = directionY;
|
||||
|
||||
path.addFirst(node.getLoc());
|
||||
node.setLoc(null);
|
||||
}
|
||||
|
||||
// Find unlocked NodeBuffer
|
||||
for (NodeBuffer buffer : holder._buffer)
|
||||
node = node.getParent();
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
private CellNodeBuffer alloc(int size)
|
||||
{
|
||||
CellNodeBuffer current = null;
|
||||
for (BufferInfo i : _buffers)
|
||||
{
|
||||
if (i.mapSize >= size)
|
||||
{
|
||||
if (!buffer.isLocked())
|
||||
for (CellNodeBuffer buf : i.buffer)
|
||||
{
|
||||
continue;
|
||||
if (buf.lock())
|
||||
{
|
||||
current = buf;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (current != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
// not found, allocate temporary buffer
|
||||
current = new CellNodeBuffer(i.mapSize);
|
||||
current.lock();
|
||||
if (i.buffer.size() < i.count)
|
||||
{
|
||||
i.buffer.add(current);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// NodeBuffer not found, allocate temporary buffer
|
||||
current = new NodeBuffer(holder._size);
|
||||
current.isLocked();
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
/**
|
||||
* NodeBuffer container with specified size and count of separate buffers.
|
||||
*/
|
||||
private static final class BufferHolder
|
||||
private static final class BufferInfo
|
||||
{
|
||||
final int _size;
|
||||
List<NodeBuffer> _buffer;
|
||||
final int mapSize;
|
||||
final int count;
|
||||
ArrayList<CellNodeBuffer> buffer;
|
||||
|
||||
public BufferHolder(int size, int count)
|
||||
public BufferInfo(int size, int cnt)
|
||||
{
|
||||
_size = size;
|
||||
_buffer = new ArrayList<>(count);
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
_buffer.add(new NodeBuffer(size));
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -1,197 +0,0 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.l2jmobius.gameserver.geoengine.geodata;
|
||||
|
||||
import java.io.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.<br>
|
||||
* 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.<br>
|
||||
* 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.<br>
|
||||
* 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.<br>
|
||||
* 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.<br>
|
||||
* 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.<br>
|
||||
* 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;
|
||||
}
|
@ -1,252 +0,0 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.l2jmobius.gameserver.geoengine.geodata;
|
||||
|
||||
import java.io.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);
|
||||
}
|
||||
}
|
@ -1,176 +0,0 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.l2jmobius.gameserver.geoengine.geodata;
|
||||
|
||||
import java.io.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));
|
||||
}
|
||||
}
|
@ -1,465 +0,0 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.l2jmobius.gameserver.geoengine.geodata;
|
||||
|
||||
import java.io.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;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,150 +0,0 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.l2jmobius.gameserver.geoengine.geodata;
|
||||
|
||||
import java.io.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)
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.l2jmobius.gameserver.geoengine.geodata;
|
||||
|
||||
/**
|
||||
* @author HorridoJoho
|
||||
*/
|
||||
public final class Cell
|
||||
{
|
||||
/** East NSWE flag */
|
||||
public static final byte NSWE_EAST = 1 << 0;
|
||||
/** West NSWE flag */
|
||||
public static final byte NSWE_WEST = 1 << 1;
|
||||
/** South NSWE flag */
|
||||
public static final byte NSWE_SOUTH = 1 << 2;
|
||||
/** North NSWE flag */
|
||||
public static final byte NSWE_NORTH = 1 << 3;
|
||||
|
||||
/** North-East NSWE flags */
|
||||
public static final byte NSWE_NORTH_EAST = NSWE_NORTH | NSWE_EAST;
|
||||
/** North-West NSWE flags */
|
||||
public static final byte NSWE_NORTH_WEST = NSWE_NORTH | NSWE_WEST;
|
||||
/** South-East NSWE flags */
|
||||
public static final byte NSWE_SOUTH_EAST = NSWE_SOUTH | NSWE_EAST;
|
||||
/** South-West NSWE flags */
|
||||
public static final byte NSWE_SOUTH_WEST = NSWE_SOUTH | NSWE_WEST;
|
||||
|
||||
/** All directions NSWE flags */
|
||||
public static final byte NSWE_ALL = NSWE_EAST | NSWE_WEST | NSWE_SOUTH | NSWE_NORTH;
|
||||
|
||||
private Cell()
|
||||
{
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.l2jmobius.gameserver.geoengine.geodata;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
}
|
@ -16,38 +16,41 @@
|
||||
*/
|
||||
package org.l2jmobius.gameserver.geoengine.geodata;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* @author Hasha
|
||||
* @author HorridoJoho
|
||||
*/
|
||||
public interface IGeoObject
|
||||
public class FlatBlock implements IBlock
|
||||
{
|
||||
/**
|
||||
* Returns geodata X coordinate of the {@link IGeoObject}.
|
||||
* @return int : Geodata X coordinate.
|
||||
*/
|
||||
int getGeoX();
|
||||
private final short _height;
|
||||
|
||||
/**
|
||||
* Returns geodata Y coordinate of the {@link IGeoObject}.
|
||||
* @return int : Geodata Y coordinate.
|
||||
*/
|
||||
int getGeoY();
|
||||
public FlatBlock(ByteBuffer bb)
|
||||
{
|
||||
_height = bb.getShort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns geodata Z coordinate of the {@link IGeoObject}.
|
||||
* @return int : Geodata Z coordinate.
|
||||
*/
|
||||
int getGeoZ();
|
||||
@Override
|
||||
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns height of the {@link IGeoObject}.
|
||||
* @return int : Height.
|
||||
*/
|
||||
int getHeight();
|
||||
@Override
|
||||
public int getNearestZ(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return _height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link IGeoObject} data.
|
||||
* @return byte[][] : {@link IGeoObject} data.
|
||||
*/
|
||||
byte[][] getObjectGeoData();
|
||||
@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;
|
||||
}
|
||||
}
|
@ -1,70 +0,0 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.l2jmobius.gameserver.geoengine.geodata;
|
||||
|
||||
import org.l2jmobius.gameserver.model.World;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.l2jmobius.gameserver.geoengine.geodata;
|
||||
|
||||
/**
|
||||
* @author HorridoJoho
|
||||
*/
|
||||
public interface IBlock
|
||||
{
|
||||
int TYPE_FLAT = 0;
|
||||
int TYPE_COMPLEX = 1;
|
||||
int TYPE_MULTILAYER = 2;
|
||||
|
||||
/** Cells in a block on the x axis */
|
||||
int BLOCK_CELLS_X = 8;
|
||||
/** Cells in a block on the y axis */
|
||||
int BLOCK_CELLS_Y = 8;
|
||||
/** Cells in a block */
|
||||
int BLOCK_CELLS = BLOCK_CELLS_X * BLOCK_CELLS_Y;
|
||||
|
||||
boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe);
|
||||
|
||||
int getNearestZ(int geoX, int geoY, int worldZ);
|
||||
|
||||
int getNextLowerZ(int geoX, int geoY, int worldZ);
|
||||
|
||||
int getNextHigherZ(int geoX, int geoY, int worldZ);
|
||||
}
|
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.l2jmobius.gameserver.geoengine.geodata;
|
||||
|
||||
/**
|
||||
* @author HorridoJoho
|
||||
*/
|
||||
public interface IRegion
|
||||
{
|
||||
/** Blocks in a region on the x axis. */
|
||||
int REGION_BLOCKS_X = 256;
|
||||
/** Blocks in a region on the y axis. */
|
||||
int REGION_BLOCKS_Y = 256;
|
||||
/** Blocks in a region. */
|
||||
int REGION_BLOCKS = REGION_BLOCKS_X * REGION_BLOCKS_Y;
|
||||
|
||||
/** Cells in a region on the x axis. */
|
||||
int REGION_CELLS_X = REGION_BLOCKS_X * IBlock.BLOCK_CELLS_X;
|
||||
/** Cells in a regioin on the y axis. */
|
||||
int REGION_CELLS_Y = REGION_BLOCKS_Y * IBlock.BLOCK_CELLS_Y;
|
||||
/** Cells in a region. */
|
||||
int REGION_CELLS = REGION_CELLS_X * REGION_CELLS_Y;
|
||||
|
||||
boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe);
|
||||
|
||||
int getNearestZ(int geoX, int geoY, int worldZ);
|
||||
|
||||
int getNextLowerZ(int geoX, int geoY, int worldZ);
|
||||
|
||||
int getNextHigherZ(int geoX, int geoY, int worldZ);
|
||||
|
||||
boolean hasGeo();
|
||||
}
|
@ -0,0 +1,183 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.l2jmobius.gameserver.geoengine.geodata;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
}
|
@ -16,52 +16,40 @@
|
||||
*/
|
||||
package org.l2jmobius.gameserver.geoengine.geodata;
|
||||
|
||||
import org.l2jmobius.gameserver.geoengine.GeoEngine;
|
||||
import org.l2jmobius.gameserver.model.Location;
|
||||
|
||||
/**
|
||||
* @author Hasha
|
||||
* @author HorridoJoho
|
||||
*/
|
||||
public class GeoLocation extends Location
|
||||
public final class NullRegion implements IRegion
|
||||
{
|
||||
private byte _nswe;
|
||||
public static final NullRegion INSTANCE = new NullRegion();
|
||||
|
||||
public GeoLocation(int x, int y, int z)
|
||||
@Override
|
||||
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
|
||||
{
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getX()
|
||||
public int getNearestZ(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return GeoEngine.getWorldX(_x);
|
||||
return worldZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getY()
|
||||
public int getNextLowerZ(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return GeoEngine.getWorldY(_y);
|
||||
return worldZ;
|
||||
}
|
||||
|
||||
public byte getNSWE()
|
||||
@Override
|
||||
public int getNextHigherZ(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return _nswe;
|
||||
return worldZ;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasGeo()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.l2jmobius.gameserver.geoengine.geodata;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* @author HorridoJoho
|
||||
*/
|
||||
public final class Region implements IRegion
|
||||
{
|
||||
private final IBlock[] _blocks = new IBlock[IRegion.REGION_BLOCKS];
|
||||
|
||||
public Region(ByteBuffer bb)
|
||||
{
|
||||
for (int blockOffset = 0; blockOffset < IRegion.REGION_BLOCKS; blockOffset++)
|
||||
{
|
||||
final int blockType = bb.get();
|
||||
switch (blockType)
|
||||
{
|
||||
case IBlock.TYPE_FLAT:
|
||||
{
|
||||
_blocks[blockOffset] = new FlatBlock(bb);
|
||||
break;
|
||||
}
|
||||
case IBlock.TYPE_COMPLEX:
|
||||
{
|
||||
_blocks[blockOffset] = new ComplexBlock(bb);
|
||||
break;
|
||||
}
|
||||
case IBlock.TYPE_MULTILAYER:
|
||||
{
|
||||
_blocks[blockOffset] = new MultilayerBlock(bb);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new RuntimeException("Invalid block type " + blockType + "!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private IBlock getBlock(int geoX, int geoY)
|
||||
{
|
||||
return _blocks[(((geoX / IBlock.BLOCK_CELLS_X) % IRegion.REGION_BLOCKS_X) * IRegion.REGION_BLOCKS_Y) + ((geoY / IBlock.BLOCK_CELLS_Y) % IRegion.REGION_BLOCKS_Y)];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkNearestNswe(int geoX, int geoY, int worldZ, int nswe)
|
||||
{
|
||||
return getBlock(geoX, geoY).checkNearestNswe(geoX, geoY, worldZ, nswe);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNearestZ(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return getBlock(geoX, geoY).getNearestZ(geoX, geoY, worldZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNextLowerZ(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return getBlock(geoX, geoY).getNextLowerZ(geoX, geoY, worldZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNextHigherZ(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return getBlock(geoX, geoY).getNextHigherZ(geoX, geoY, worldZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasGeo()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
@ -0,0 +1,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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.l2jmobius.gameserver.geoengine.pathfinding;
|
||||
|
||||
public abstract class AbstractNode<T extends AbstractNodeLoc>
|
||||
{
|
||||
private T _loc;
|
||||
private AbstractNode<T> _parent;
|
||||
|
||||
public AbstractNode(T loc)
|
||||
{
|
||||
_loc = loc;
|
||||
}
|
||||
|
||||
public void setParent(AbstractNode<T> p)
|
||||
{
|
||||
_parent = p;
|
||||
}
|
||||
|
||||
public AbstractNode<T> 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;
|
||||
}
|
||||
}
|
@ -14,26 +14,20 @@
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.l2jmobius.gameserver.geoengine.geodata;
|
||||
package org.l2jmobius.gameserver.geoengine.pathfinding;
|
||||
|
||||
/**
|
||||
* @author Hasha
|
||||
* @author -Nemesiss-
|
||||
*/
|
||||
public enum GeoFormat
|
||||
public abstract class AbstractNodeLoc
|
||||
{
|
||||
L2J("%d_%d.l2j"),
|
||||
L2OFF("%d_%d_conv.dat"),
|
||||
L2D("%d_%d.l2d");
|
||||
public abstract int getX();
|
||||
|
||||
private final String _filename;
|
||||
public abstract int getY();
|
||||
|
||||
private GeoFormat(String filename)
|
||||
{
|
||||
_filename = filename;
|
||||
}
|
||||
public abstract int getZ();
|
||||
|
||||
public String getFilename()
|
||||
{
|
||||
return _filename;
|
||||
}
|
||||
}
|
||||
public abstract int getNodeX();
|
||||
|
||||
public abstract int getNodeY();
|
||||
}
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.l2jmobius.gameserver.geoengine.pathfinding;
|
||||
|
||||
public class CellNode extends AbstractNode<NodeLoc>
|
||||
{
|
||||
private CellNode _next = null;
|
||||
private boolean _isInUse = true;
|
||||
private float _cost = -1000;
|
||||
|
||||
public CellNode(NodeLoc loc)
|
||||
{
|
||||
super(loc);
|
||||
}
|
||||
|
||||
public boolean isInUse()
|
||||
{
|
||||
return _isInUse;
|
||||
}
|
||||
|
||||
public void setInUse()
|
||||
{
|
||||
_isInUse = true;
|
||||
}
|
||||
|
||||
public CellNode getNext()
|
||||
{
|
||||
return _next;
|
||||
}
|
||||
|
||||
public void setNext(CellNode next)
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public float getCost()
|
||||
{
|
||||
return _cost;
|
||||
}
|
||||
|
||||
public void setCost(double cost)
|
||||
{
|
||||
_cost = (float) cost;
|
||||
}
|
||||
|
||||
public void free()
|
||||
{
|
||||
setParent(null);
|
||||
_cost = -1000;
|
||||
_isInUse = false;
|
||||
_next = null;
|
||||
}
|
||||
}
|
@ -0,0 +1,348 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.l2jmobius.gameserver.geoengine.pathfinding;
|
||||
|
||||
import java.util.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<CellNode> debugPath()
|
||||
{
|
||||
final List<CellNode> result = new LinkedList<>();
|
||||
|
||||
for (CellNode n = _current; n.getParent() != null; n = (CellNode) n.getParent())
|
||||
{
|
||||
result.add(n);
|
||||
n.setCost(-n.getCost());
|
||||
}
|
||||
|
||||
for (int i = 0; i < _mapSize; i++)
|
||||
{
|
||||
for (int j = 0; j < _mapSize; j++)
|
||||
{
|
||||
final CellNode n = _buffer[i][j];
|
||||
if ((n == null) || !n.isInUse() || (n.getCost() <= 0))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
result.add(n);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private 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;
|
||||
}
|
||||
}
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
@ -1,306 +0,0 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.l2jmobius.gameserver.geoengine.pathfinding;
|
||||
|
||||
import java.util.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
|
||||
}
|
||||
}
|
@ -0,0 +1,183 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.l2jmobius.gameserver.geoengine.pathfinding;
|
||||
|
||||
import org.l2jmobius.gameserver.geoengine.GeoEngine;
|
||||
import org.l2jmobius.gameserver.geoengine.geodata.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;
|
||||
}
|
||||
}
|
@ -63,6 +63,8 @@ import org.l2jmobius.gameserver.enums.Team;
|
||||
import org.l2jmobius.gameserver.enums.TeleportWhereType;
|
||||
import org.l2jmobius.gameserver.enums.UserInfoType;
|
||||
import org.l2jmobius.gameserver.geoengine.GeoEngine;
|
||||
import org.l2jmobius.gameserver.geoengine.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;
|
||||
@ -250,7 +252,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
||||
/** Movement data of this Creature */
|
||||
protected MoveData _move;
|
||||
private boolean _cursorKeyMovement = false;
|
||||
private boolean _cursorKeyMovementActive = true;
|
||||
|
||||
/** This creature's target. */
|
||||
private WorldObject _target;
|
||||
@ -2557,7 +2558,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
||||
|
||||
public boolean disregardingGeodata;
|
||||
public int onGeodataPathIndex;
|
||||
public List<Location> geoPath;
|
||||
public List<AbstractNodeLoc> geoPath;
|
||||
public int geoPathAccurateTx;
|
||||
public int geoPathAccurateTy;
|
||||
public int geoPathGtx;
|
||||
@ -3018,7 +3019,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
||||
{
|
||||
_move.onGeodataPathIndex = -1;
|
||||
stopMove(getActingPlayer().getLastServerPosition());
|
||||
_cursorKeyMovementActive = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -3259,10 +3259,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
||||
double dy = (y - curY);
|
||||
double dz = (z - curZ);
|
||||
double distance = Math.hypot(dx, dy);
|
||||
if (!_cursorKeyMovementActive && (distance > 200))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final boolean verticalMovementOnly = _isFlying && (distance == 0) && (dz != 0);
|
||||
if (verticalMovementOnly)
|
||||
@ -3392,7 +3388,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
||||
if (((originalDistance - distance) > 30) && !isControlBlocked() && !isInVehicle)
|
||||
{
|
||||
// Path calculation -- overrides previous movement check
|
||||
m.geoPath = GeoEngine.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld());
|
||||
m.geoPath = GeoEnginePathfinding.getInstance().findPath(curX, curY, curZ, originalX, originalY, originalZ, getInstanceWorld());
|
||||
if ((m.geoPath == null) || (m.geoPath.size() < 2)) // No path found
|
||||
{
|
||||
m.disregardingGeodata = true;
|
||||
@ -5475,16 +5471,6 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe
|
||||
_cursorKeyMovement = value;
|
||||
}
|
||||
|
||||
public void setCursorKeyMovementActive(boolean value)
|
||||
{
|
||||
_cursorKeyMovementActive = value;
|
||||
}
|
||||
|
||||
public boolean isCursorKeyMovementActive()
|
||||
{
|
||||
return _cursorKeyMovementActive;
|
||||
}
|
||||
|
||||
public List<ItemInstance> getFakePlayerDrops()
|
||||
{
|
||||
return _fakePlayerDrops;
|
||||
|
@ -12711,7 +12711,7 @@ public class PlayerInstance extends Playable
|
||||
{
|
||||
if (Config.CORRECT_PLAYER_Z)
|
||||
{
|
||||
final int nearestZ = GeoEngine.getInstance().getHeightNearest(getX(), getY(), getZ());
|
||||
final int nearestZ = GeoEngine.getInstance().getNextLowerZ(getX(), getY(), getZ());
|
||||
if (getZ() < nearestZ)
|
||||
{
|
||||
teleToLocation(new Location(getX(), getY(), nearestZ));
|
||||
|
@ -174,10 +174,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket
|
||||
// sort of incompatibility fix.
|
||||
// Validate position packets sends head level.
|
||||
_targetZ += player.getTemplate().getCollisionHeight();
|
||||
if (!player.isCursorKeyMovementActive() && (player.isInFrontOf(new Location(_targetX, _targetY, _targetZ)) || player.isOnSideOf(new Location(_originX, _originY, _originZ))))
|
||||
{
|
||||
player.setCursorKeyMovementActive(true);
|
||||
}
|
||||
|
||||
if (_movementMode == 1)
|
||||
{
|
||||
@ -196,10 +192,6 @@ public class MoveBackwardToLocation implements IClientIncomingPacket
|
||||
return;
|
||||
}
|
||||
player.setCursorKeyMovement(true);
|
||||
if (!player.isCursorKeyMovementActive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final AdminTeleportType teleMode = player.getTeleMode();
|
||||
|
@ -19,32 +19,32 @@ package org.l2jmobius.gameserver.util;
|
||||
import java.awt.Color;
|
||||
|
||||
import org.l2jmobius.gameserver.geoengine.GeoEngine;
|
||||
import org.l2jmobius.gameserver.geoengine.geodata.GeoStructure;
|
||||
import org.l2jmobius.gameserver.geoengine.geodata.Cell;
|
||||
import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
|
||||
import org.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive;
|
||||
|
||||
/**
|
||||
* @author HorridoJoho
|
||||
*/
|
||||
public class GeoUtils
|
||||
public final class GeoUtils
|
||||
{
|
||||
public static void debug2DLine(PlayerInstance player, int x, int y, int tx, int ty, int z)
|
||||
{
|
||||
final int gx = GeoEngine.getGeoX(x);
|
||||
final int gy = GeoEngine.getGeoY(y);
|
||||
final int gx = GeoEngine.getInstance().getGeoX(x);
|
||||
final int gy = GeoEngine.getInstance().getGeoY(y);
|
||||
|
||||
final int tgx = GeoEngine.getGeoX(tx);
|
||||
final int tgy = GeoEngine.getGeoY(ty);
|
||||
final int tgx = GeoEngine.getInstance().getGeoX(tx);
|
||||
final int tgy = GeoEngine.getInstance().getGeoY(ty);
|
||||
|
||||
final ExServerPrimitive prim = new ExServerPrimitive("Debug2DLine", x, y, z);
|
||||
prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), z);
|
||||
prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), z);
|
||||
|
||||
final LinePointIterator iter = new LinePointIterator(gx, gy, tgx, tgy);
|
||||
|
||||
while (iter.next())
|
||||
{
|
||||
final int wx = GeoEngine.getWorldX(iter.x());
|
||||
final int wy = GeoEngine.getWorldY(iter.y());
|
||||
final int wx = GeoEngine.getInstance().getWorldX(iter.x());
|
||||
final int wy = GeoEngine.getInstance().getWorldY(iter.y());
|
||||
|
||||
prim.addPoint(Color.RED, wx, wy, z);
|
||||
}
|
||||
@ -53,21 +53,21 @@ public class GeoUtils
|
||||
|
||||
public static void debug3DLine(PlayerInstance player, int x, int y, int z, int tx, int ty, int tz)
|
||||
{
|
||||
final int gx = GeoEngine.getGeoX(x);
|
||||
final int gy = GeoEngine.getGeoY(y);
|
||||
final int gx = GeoEngine.getInstance().getGeoX(x);
|
||||
final int gy = GeoEngine.getInstance().getGeoY(y);
|
||||
|
||||
final int tgx = GeoEngine.getGeoX(tx);
|
||||
final int tgy = GeoEngine.getGeoY(ty);
|
||||
final int tgx = GeoEngine.getInstance().getGeoX(tx);
|
||||
final int tgy = GeoEngine.getInstance().getGeoY(ty);
|
||||
|
||||
final ExServerPrimitive prim = new ExServerPrimitive("Debug3DLine", x, y, z);
|
||||
prim.addLine(Color.BLUE, GeoEngine.getWorldX(gx), GeoEngine.getWorldY(gy), z, GeoEngine.getWorldX(tgx), GeoEngine.getWorldY(tgy), tz);
|
||||
prim.addLine(Color.BLUE, GeoEngine.getInstance().getWorldX(gx), GeoEngine.getInstance().getWorldY(gy), z, GeoEngine.getInstance().getWorldX(tgx), GeoEngine.getInstance().getWorldY(tgy), tz);
|
||||
|
||||
final LinePointIterator3D iter = new LinePointIterator3D(gx, gy, z, tgx, tgy, tz);
|
||||
iter.next();
|
||||
int prevX = iter.x();
|
||||
int prevY = iter.y();
|
||||
int wx = GeoEngine.getWorldX(prevX);
|
||||
int wy = GeoEngine.getWorldY(prevY);
|
||||
int wx = GeoEngine.getInstance().getWorldX(prevX);
|
||||
int wy = GeoEngine.getInstance().getWorldY(prevY);
|
||||
int wz = iter.z();
|
||||
prim.addPoint(Color.RED, wx, wy, wz);
|
||||
|
||||
@ -78,8 +78,8 @@ public class GeoUtils
|
||||
|
||||
if ((curX != prevX) || (curY != prevY))
|
||||
{
|
||||
wx = GeoEngine.getWorldX(curX);
|
||||
wy = GeoEngine.getWorldY(curY);
|
||||
wx = GeoEngine.getInstance().getWorldX(curX);
|
||||
wy = GeoEngine.getInstance().getWorldY(curY);
|
||||
wz = iter.z();
|
||||
|
||||
prim.addPoint(Color.RED, wx, wy, wz);
|
||||
@ -93,7 +93,7 @@ public class GeoUtils
|
||||
|
||||
private static Color getDirectionColor(int x, int y, int z, int nswe)
|
||||
{
|
||||
if ((GeoEngine.getInstance().getNsweNearest(x, y, z) & nswe) == nswe)
|
||||
if (GeoEngine.getInstance().checkNearestNswe(x, y, z, nswe))
|
||||
{
|
||||
return Color.GREEN;
|
||||
}
|
||||
@ -109,8 +109,9 @@ public class GeoUtils
|
||||
int iPacket = 0;
|
||||
|
||||
ExServerPrimitive exsp = null;
|
||||
final int playerGx = GeoEngine.getGeoX(player.getX());
|
||||
final int playerGy = GeoEngine.getGeoY(player.getY());
|
||||
final GeoEngine ge = GeoEngine.getInstance();
|
||||
final int playerGx = ge.getGeoX(player.getX());
|
||||
final int playerGy = ge.getGeoY(player.getY());
|
||||
for (int dx = -geoRadius; dx <= geoRadius; ++dx)
|
||||
{
|
||||
for (int dy = -geoRadius; dy <= geoRadius; ++dy)
|
||||
@ -134,32 +135,32 @@ public class GeoUtils
|
||||
final int gx = playerGx + dx;
|
||||
final int gy = playerGy + dy;
|
||||
|
||||
final int x = GeoEngine.getWorldX(gx);
|
||||
final int y = GeoEngine.getWorldY(gy);
|
||||
final int z = GeoEngine.getInstance().getHeightNearest(gx, gy, player.getZ());
|
||||
final int x = ge.getWorldX(gx);
|
||||
final int y = ge.getWorldY(gy);
|
||||
final int z = ge.getNearestZ(gx, gy, player.getZ());
|
||||
|
||||
// north arrow
|
||||
Color col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_N);
|
||||
Color col = getDirectionColor(gx, gy, z, Cell.NSWE_NORTH);
|
||||
exsp.addLine(col, x - 1, y - 7, z, x + 1, y - 7, z);
|
||||
exsp.addLine(col, x - 2, y - 6, z, x + 2, y - 6, z);
|
||||
exsp.addLine(col, x - 3, y - 5, z, x + 3, y - 5, z);
|
||||
exsp.addLine(col, x - 4, y - 4, z, x + 4, y - 4, z);
|
||||
|
||||
// east arrow
|
||||
col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_E);
|
||||
col = getDirectionColor(gx, gy, z, Cell.NSWE_EAST);
|
||||
exsp.addLine(col, x + 7, y - 1, z, x + 7, y + 1, z);
|
||||
exsp.addLine(col, x + 6, y - 2, z, x + 6, y + 2, z);
|
||||
exsp.addLine(col, x + 5, y - 3, z, x + 5, y + 3, z);
|
||||
exsp.addLine(col, x + 4, y - 4, z, x + 4, y + 4, z);
|
||||
|
||||
// south arrow
|
||||
col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_S);
|
||||
col = getDirectionColor(gx, gy, z, Cell.NSWE_SOUTH);
|
||||
exsp.addLine(col, x - 1, y + 7, z, x + 1, y + 7, z);
|
||||
exsp.addLine(col, x - 2, y + 6, z, x + 2, y + 6, z);
|
||||
exsp.addLine(col, x - 3, y + 5, z, x + 3, y + 5, z);
|
||||
exsp.addLine(col, x - 4, y + 4, z, x + 4, y + 4, z);
|
||||
|
||||
col = getDirectionColor(gx, gy, z, GeoStructure.CELL_FLAG_W);
|
||||
col = getDirectionColor(gx, gy, z, Cell.NSWE_WEST);
|
||||
exsp.addLine(col, x - 7, y - 1, z, x - 7, y + 1, z);
|
||||
exsp.addLine(col, x - 6, y - 2, z, x - 6, y + 2, z);
|
||||
exsp.addLine(col, x - 5, y - 3, z, x - 5, y + 3, z);
|
||||
@ -187,42 +188,41 @@ public class GeoUtils
|
||||
{
|
||||
if (y > lastY)
|
||||
{
|
||||
return GeoStructure.CELL_FLAG_SE; // Direction.SOUTH_EAST;
|
||||
return Cell.NSWE_SOUTH_EAST;
|
||||
}
|
||||
else if (y < lastY)
|
||||
{
|
||||
return GeoStructure.CELL_FLAG_NE; // Direction.NORTH_EAST;
|
||||
return Cell.NSWE_NORTH_EAST;
|
||||
}
|
||||
else
|
||||
{
|
||||
return GeoStructure.CELL_FLAG_E; // Direction.EAST;
|
||||
return Cell.NSWE_EAST;
|
||||
}
|
||||
}
|
||||
else if (x < lastX) // west
|
||||
{
|
||||
if (y > lastY)
|
||||
{
|
||||
return GeoStructure.CELL_FLAG_SW; // Direction.SOUTH_WEST;
|
||||
return Cell.NSWE_SOUTH_WEST;
|
||||
}
|
||||
else if (y < lastY)
|
||||
{
|
||||
return GeoStructure.CELL_FLAG_NW; // Direction.NORTH_WEST;
|
||||
return Cell.NSWE_NORTH_WEST;
|
||||
}
|
||||
else
|
||||
{
|
||||
return GeoStructure.CELL_FLAG_W; // Direction.WEST;
|
||||
return Cell.NSWE_WEST;
|
||||
}
|
||||
}
|
||||
else
|
||||
// unchanged x
|
||||
else // unchanged x
|
||||
{
|
||||
if (y > lastY)
|
||||
{
|
||||
return GeoStructure.CELL_FLAG_S; // Direction.SOUTH;
|
||||
return Cell.NSWE_SOUTH;
|
||||
}
|
||||
else if (y < lastY)
|
||||
{
|
||||
return GeoStructure.CELL_FLAG_N; // Direction.NORTH;
|
||||
return Cell.NSWE_NORTH;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1,386 +0,0 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package org.l2jmobius.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);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user