Reverted back to l2j geoengine.

This commit is contained in:
MobiusDevelopment
2021-02-20 07:48:16 +00:00
parent 4c92ceeabc
commit dd0d74a8e6
794 changed files with 45461 additions and 70442 deletions

View File

@@ -1,6 +0,0 @@
@echo off
title L2D geodata converter
java -Xmx512m -cp ./../libs/* org.l2jmobius.tools.geodataconverter.GeoDataConverter
pause

View File

@@ -1,4 +0,0 @@
#! /bin/sh
java -Xmx512m -cp ../libs/*: org.l2jmobius.tools.geodataconverter.GeoDataConverter > log/stdout.log 2>&1

View File

@@ -1,10 +1,6 @@
# =================================================================
# Geodata
# =================================================================
# Because of real-time performance we are using geodata files only in
# diagonal L2D format now (using filename e.g. 22_16.l2d).
# L2D geodata can be obtained by conversion of existing L2J or L2OFF geodata.
# Launch "GeoDataConverter.bat/sh" and follow instructions to start the conversion.
# Specifies the path to geodata files. For example, when using geodata files located
# at different folder/harddrive ("C:/Program Files/Lineage II/system/geodata/"), default: ./data/geodata/
@@ -16,16 +12,6 @@ GeoDataPath = ./data/geodata/
# -1 - Old system: will synchronize Z only
CoordSynchronize = 2
# =================================================================
# Path checking
# =================================================================
# Line of sight start at X percent of the character height, default: 75
PartOfCharacterHeight = 75
# Maximum height of an obstacle, which can exceed the line of sight, default: 32
MaxObstacleHeight = 32
# =================================================================
# Path finding
# =================================================================
@@ -37,22 +23,18 @@ PathFinding = true
# Pathfinding array buffers configuration, default: 100x6;128x6;192x6;256x4;320x4;384x4;500x2
PathFindBuffers = 100x6;128x6;192x6;256x4;320x4;384x4;500x2
# Base path weight, when moving from one node to another on axis direction, default: 10
BaseWeight = 10
# Weight for nodes without obstacles far from walls
LowWeight = 0.5
# Path weight, when moving from one node to another on diagonal direction, default: BaseWeight * sqrt(2) = 14
DiagonalWeight = 14
# Weight for nodes near walls
MediumWeight = 2
# When movement flags of target node is blocked to any direction, multiply movement weight by this multiplier.
# This causes pathfinding algorithm to avoid path construction exactly near an obstacle, default: 10
ObstacleMultiplier = 10
# Weight for nodes with obstacles
HighWeight = 3
# Weight of the heuristic algorithm, which is giving estimated cost from node to target, default: 20
# For proper function must be higher than BaseWeight and/or DiagonalWeight.
HeuristicWeight = 20
# Maximum number of generated nodes per one path-finding process, default 3500
MaxIterations = 3500
# Weight for diagonal movement.
# Default: LowWeight * sqrt(2)
DiagonalWeight = 0.707
# =================================================================
# Other

View File

@@ -55,8 +55,9 @@ public class AdminGeodata implements IAdminCommandHandler
final int worldX = activeChar.getX();
final int worldY = activeChar.getY();
final int worldZ = activeChar.getZ();
final int geoX = GeoEngine.getGeoX(worldX);
final int geoY = GeoEngine.getGeoY(worldY);
final int geoX = GeoEngine.getInstance().getGeoX(worldX);
final int geoY = GeoEngine.getInstance().getGeoY(worldY);
if (GeoEngine.getInstance().hasGeoPos(geoX, geoY))
{
BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ));
@@ -72,8 +73,9 @@ public class AdminGeodata implements IAdminCommandHandler
final int worldX = activeChar.getX();
final int worldY = activeChar.getY();
final int worldZ = activeChar.getZ();
final int geoX = GeoEngine.getGeoX(worldX);
final int geoY = GeoEngine.getGeoY(worldY);
final int geoX = GeoEngine.getInstance().getGeoX(worldX);
final int geoY = GeoEngine.getInstance().getGeoY(worldY);
if (GeoEngine.getInstance().hasGeoPos(geoX, geoY))
{
BuilderUtil.sendSysMessage(activeChar, "WorldX: " + worldX + ", WorldY: " + worldY + ", WorldZ: " + worldZ + ", GeoX: " + geoX + ", GeoY: " + geoY + ", GeoZ: " + GeoEngine.getInstance().getHeight(worldX, worldY, worldZ));

View File

@@ -18,18 +18,18 @@ package handlers.admincommandhandlers;
import java.util.List;
import org.l2jmobius.gameserver.geoengine.GeoEngine;
import org.l2jmobius.Config;
import org.l2jmobius.gameserver.geoengine.GeoEnginePathfinding;
import org.l2jmobius.gameserver.geoengine.pathfinding.AbstractNodeLoc;
import org.l2jmobius.gameserver.handler.IAdminCommandHandler;
import org.l2jmobius.gameserver.model.Location;
import org.l2jmobius.gameserver.model.actor.instance.PlayerInstance;
import org.l2jmobius.gameserver.network.SystemMessageId;
import org.l2jmobius.gameserver.util.BuilderUtil;
public class AdminPathNode implements IAdminCommandHandler
{
private static final String[] ADMIN_COMMANDS =
{
"admin_path_find",
"admin_path_find"
};
@Override
@@ -37,30 +37,29 @@ public class AdminPathNode implements IAdminCommandHandler
{
if (command.equals("admin_path_find"))
{
if (!Config.PATHFINDING)
{
BuilderUtil.sendSysMessage(activeChar, "PathFinding is disabled.");
return true;
}
if (activeChar.getTarget() != null)
{
final List<Location> path = GeoEngine.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld());
final List<AbstractNodeLoc> path = GeoEnginePathfinding.getInstance().findPath(activeChar.getX(), activeChar.getY(), (short) activeChar.getZ(), activeChar.getTarget().getX(), activeChar.getTarget().getY(), (short) activeChar.getTarget().getZ(), activeChar.getInstanceWorld());
if (path == null)
{
BuilderUtil.sendSysMessage(activeChar, "No route found or pathfinding disabled.");
BuilderUtil.sendSysMessage(activeChar, "No Route!");
return true;
}
else
for (AbstractNodeLoc a : path)
{
for (Location point : path)
{
BuilderUtil.sendSysMessage(activeChar, "x:" + point.getX() + " y:" + point.getY() + " z:" + point.getZ());
}
BuilderUtil.sendSysMessage(activeChar, "x:" + a.getX() + " y:" + a.getY() + " z:" + a.getZ());
}
}
else
{
activeChar.sendPacket(SystemMessageId.INVALID_TARGET);
BuilderUtil.sendSysMessage(activeChar, "No Target!");
}
}
else
{
return false;
}
return true;
}

View File

@@ -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;
@@ -904,17 +905,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 */
@@ -2418,17 +2416,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)

View File

@@ -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();
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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));
}
}

View File

@@ -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;
}
}
}

View File

@@ -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)
{
}
}

View File

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

View File

@@ -0,0 +1,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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

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

View File

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

View File

@@ -0,0 +1,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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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();
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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
}
}

View File

@@ -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;
}
}

View File

@@ -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;
@@ -5474,16 +5470,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;

View File

@@ -12509,7 +12509,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));

View File

@@ -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();

View File

@@ -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
{

View File

@@ -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);
}
}

View File

@@ -6,7 +6,8 @@ System: https://www.mediafire.com/file/mvergqghqz4jzpq/L2J_Mobius_Classic_Interl
JDK: http://www.mediafire.com/file/cgh3zupv80qdwv4/bellsoft-jdk15.0.2%252B10-windows-amd64.msi
Eclipse: http://www.mediafire.com/file/h0gmazpv9hm6gjp/eclipse-java-2020-12-R-win32-x86_64.zip
Geodata: https://www.mediafire.com/file/5ebydcm5ipckpoa/L2J_Mobius_Classic_Interlude_Geodata_v5.zip
Geodata: http://www.mediafire.com/file/prx3sls7lts5p1f/L2J_Mobius_Classic_Interlude_Geodata.zip
This is a Classic server based on the Grand Crusade client.
The goal is to make a better approximation of what Classic is to older chronicles, like Interlude.
@@ -17,6 +18,7 @@ Who knows? Maybe some day it will be a pure Interlude version.
A lot of things can go wrong while using this project,
if you do not know what you are doing, it is best not to use it.
Tools that might be helpful (use with Java 1.8)
L2ClientDat: https://github.com/MobiusDevelopment/l2clientdat
XdatEditor: https://github.com/MobiusDevelopment/xdat_editor