Diagonal movement GeoEngine.
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,308 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General 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 com.l2jmobius.gameserver.geoengine;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import com.l2jmobius.commons.util.StringUtil;
|
||||
import com.l2jmobius.gameserver.geoengine.geodata.GeoLocation;
|
||||
import com.l2jmobius.gameserver.geoengine.pathfinding.Node;
|
||||
import com.l2jmobius.gameserver.geoengine.pathfinding.NodeBuffer;
|
||||
import com.l2jmobius.gameserver.model.Location;
|
||||
import com.l2jmobius.gameserver.model.instancezone.Instance;
|
||||
|
||||
/**
|
||||
* @author Hasha
|
||||
*/
|
||||
final class GeoEnginePathfinding extends GeoEngine
|
||||
{
|
||||
// pre-allocated buffers
|
||||
private final BufferHolder[] _buffers;
|
||||
|
||||
protected GeoEnginePathfinding()
|
||||
{
|
||||
super();
|
||||
|
||||
String[] array = Config.PATHFIND_BUFFERS.split(";");
|
||||
_buffers = new BufferHolder[array.length];
|
||||
|
||||
int count = 0;
|
||||
for (int i = 0; i < array.length; i++)
|
||||
{
|
||||
String buf = array[i];
|
||||
String[] args = buf.split("x");
|
||||
|
||||
try
|
||||
{
|
||||
int size = Integer.parseInt(args[1]);
|
||||
count += size;
|
||||
_buffers[i] = new BufferHolder(Integer.parseInt(args[0]), size);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.warning("GeoEnginePathfinding: Can not load buffer setting: " + buf);
|
||||
}
|
||||
}
|
||||
|
||||
_log.info("GeoEnginePathfinding: Loaded " + count + " node buffers.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Location> findPath(int ox, int oy, int oz, int tx, int ty, int tz, Instance instance, boolean playable)
|
||||
{
|
||||
// get origin and check existing geo coords
|
||||
int gox = getGeoX(ox);
|
||||
int goy = getGeoY(oy);
|
||||
if (!hasGeoPos(gox, goy))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
short goz = getHeightNearest(gox, goy, oz);
|
||||
|
||||
// get target and check existing geo coords
|
||||
int gtx = getGeoX(tx);
|
||||
int gty = getGeoY(ty);
|
||||
if (!hasGeoPos(gtx, gty))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
short gtz = getHeightNearest(gtx, gty, tz);
|
||||
|
||||
// Prepare buffer for pathfinding calculations
|
||||
NodeBuffer buffer = getBuffer(64 + (2 * Math.max(Math.abs(gox - gtx), Math.abs(goy - gty))), playable);
|
||||
if (buffer == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// find path
|
||||
List<Location> path = null;
|
||||
try
|
||||
{
|
||||
Node result = buffer.findPath(gox, goy, goz, gtx, gty, gtz);
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
path = constructPath(result);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.warning(e.getMessage());
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
buffer.free();
|
||||
}
|
||||
|
||||
// check path
|
||||
if (path.size() < 3)
|
||||
{
|
||||
return path;
|
||||
}
|
||||
|
||||
// get path list iterator
|
||||
ListIterator<Location> point = path.listIterator();
|
||||
|
||||
// get node A (origin)
|
||||
int nodeAx = gox;
|
||||
int nodeAy = goy;
|
||||
short nodeAz = goz;
|
||||
|
||||
// get node B
|
||||
GeoLocation nodeB = (GeoLocation) point.next();
|
||||
|
||||
// iterate thought the path to optimize it
|
||||
while (point.hasNext())
|
||||
{
|
||||
// get node C
|
||||
GeoLocation nodeC = (GeoLocation) path.get(point.nextIndex());
|
||||
|
||||
// check movement from node A to node C
|
||||
GeoLocation loc = checkMove(nodeAx, nodeAy, nodeAz, nodeC.getGeoX(), nodeC.getGeoY(), nodeC.getZ(), instance);
|
||||
if ((loc.getGeoX() == nodeC.getGeoX()) && (loc.getGeoY() == nodeC.getGeoY()))
|
||||
{
|
||||
// can move from node A to node C
|
||||
|
||||
// remove node B
|
||||
point.remove();
|
||||
}
|
||||
else
|
||||
{
|
||||
// can not move from node A to node C
|
||||
|
||||
// set node A (node B is part of path, update A coordinates)
|
||||
nodeAx = nodeB.getGeoX();
|
||||
nodeAy = nodeB.getGeoY();
|
||||
nodeAz = (short) nodeB.getZ();
|
||||
}
|
||||
|
||||
// set node B
|
||||
nodeB = (GeoLocation) point.next();
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create list of node locations as result of calculated buffer node tree.
|
||||
* @param target : the entry point
|
||||
* @return List<NodeLoc> : list of node location
|
||||
*/
|
||||
private static final List<Location> constructPath(Node target)
|
||||
{
|
||||
// create empty list
|
||||
LinkedList<Location> list = new LinkedList<>();
|
||||
|
||||
// set direction X/Y
|
||||
int dx = 0;
|
||||
int dy = 0;
|
||||
|
||||
// get target parent
|
||||
Node parent = target.getParent();
|
||||
|
||||
// while parent exists
|
||||
while (parent != null)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides optimize selection of the buffer. When all pre-initialized buffer are locked, creates new buffer and log this situation.
|
||||
* @param size : pre-calculated minimal required size
|
||||
* @param playable : moving object is playable?
|
||||
* @return NodeBuffer : buffer
|
||||
*/
|
||||
private final NodeBuffer getBuffer(int size, boolean playable)
|
||||
{
|
||||
NodeBuffer current = null;
|
||||
for (BufferHolder holder : _buffers)
|
||||
{
|
||||
// Find proper size of buffer
|
||||
if (holder._size < size)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find unlocked NodeBuffer
|
||||
for (NodeBuffer buffer : holder._buffer)
|
||||
{
|
||||
if (!buffer.isLocked())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
holder._uses++;
|
||||
if (playable)
|
||||
{
|
||||
holder._playableUses++;
|
||||
}
|
||||
|
||||
holder._elapsed += buffer.getElapsedTime();
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// NodeBuffer not found, allocate temporary buffer
|
||||
current = new NodeBuffer(holder._size);
|
||||
current.isLocked();
|
||||
|
||||
holder._overflows++;
|
||||
if (playable)
|
||||
{
|
||||
holder._playableOverflows++;
|
||||
}
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
/**
|
||||
* NodeBuffer container with specified size and count of separate buffers.
|
||||
*/
|
||||
private static final class BufferHolder
|
||||
{
|
||||
final int _size;
|
||||
final int _count;
|
||||
ArrayList<NodeBuffer> _buffer;
|
||||
|
||||
// statistics
|
||||
int _playableUses = 0;
|
||||
int _uses = 0;
|
||||
int _playableOverflows = 0;
|
||||
int _overflows = 0;
|
||||
long _elapsed = 0;
|
||||
|
||||
public BufferHolder(int size, int count)
|
||||
{
|
||||
_size = size;
|
||||
_count = count;
|
||||
_buffer = new ArrayList<>(count);
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
_buffer.add(new NodeBuffer(size));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
final StringBuilder sb = new StringBuilder(100);
|
||||
|
||||
StringUtil.append(sb, "Buffer ", String.valueOf(_size), "x", String.valueOf(_size), ": count=", String.valueOf(_count), " uses=", String.valueOf(_playableUses), "/", String.valueOf(_uses));
|
||||
|
||||
if (_uses > 0)
|
||||
{
|
||||
StringUtil.append(sb, " total/avg(ms)=", String.valueOf(_elapsed), "/", String.format("%1.2f", (double) _elapsed / _uses));
|
||||
}
|
||||
|
||||
StringUtil.append(sb, " ovf=", String.valueOf(_playableOverflows), "/", String.valueOf(_overflows));
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,197 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.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;
|
||||
}
|
@@ -0,0 +1,252 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.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 final boolean hasGeoPos()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final 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 final 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 final 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 final 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 final 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 final 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 final int getIndexNearest(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return (((geoX % GeoStructure.BLOCK_CELLS_X) * GeoStructure.BLOCK_CELLS_Y) + (geoY % GeoStructure.BLOCK_CELLS_Y)) * 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final 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 final 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 final 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 final byte getNswe(int index)
|
||||
{
|
||||
return _buffer[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getNsweOriginal(int index)
|
||||
{
|
||||
return _buffer[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setNswe(int index, byte nswe)
|
||||
{
|
||||
_buffer[index] = nswe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final 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);
|
||||
}
|
||||
}
|
@@ -0,0 +1,255 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General 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 com.l2jmobius.gameserver.geoengine.geodata;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Hasha
|
||||
*/
|
||||
public final class BlockComplexDynamic extends BlockComplex implements IBlockDynamic
|
||||
{
|
||||
private final int _bx;
|
||||
private final int _by;
|
||||
private final byte[] _original;
|
||||
private final List<IGeoObject> _objects;
|
||||
|
||||
/**
|
||||
* Creates {@link BlockComplexDynamic}.
|
||||
* @param bx : Block X coordinate.
|
||||
* @param by : Block Y coordinate.
|
||||
* @param block : The original FlatBlock to create a dynamic version from.
|
||||
*/
|
||||
public BlockComplexDynamic(int bx, int by, BlockFlat block)
|
||||
{
|
||||
// load data
|
||||
final byte nswe = block._nswe;
|
||||
final byte heightLow = (byte) (block._height & 0x00FF);
|
||||
final byte heightHigh = (byte) (block._height >> 8);
|
||||
|
||||
// initialize buffer
|
||||
_buffer = new byte[GeoStructure.BLOCK_CELLS * 3];
|
||||
|
||||
// save data
|
||||
for (int i = 0; i < GeoStructure.BLOCK_CELLS; i++)
|
||||
{
|
||||
// set nswe
|
||||
_buffer[i * 3] = nswe;
|
||||
|
||||
// set height
|
||||
_buffer[(i * 3) + 1] = heightLow;
|
||||
_buffer[(i * 3) + 2] = heightHigh;
|
||||
}
|
||||
|
||||
// get block coordinates
|
||||
_bx = bx;
|
||||
_by = by;
|
||||
|
||||
// create copy for dynamic implementation
|
||||
_original = new byte[GeoStructure.BLOCK_CELLS * 3];
|
||||
System.arraycopy(_buffer, 0, _original, 0, GeoStructure.BLOCK_CELLS * 3);
|
||||
|
||||
// create list for geo objects
|
||||
_objects = new LinkedList<>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates {@link BlockComplexDynamic}.
|
||||
* @param bx : Block X coordinate.
|
||||
* @param by : Block Y coordinate.
|
||||
* @param block : The original ComplexBlock to create a dynamic version from.
|
||||
*/
|
||||
public BlockComplexDynamic(int bx, int by, BlockComplex block)
|
||||
{
|
||||
// move buffer from BlockComplex object to this object
|
||||
_buffer = block._buffer;
|
||||
block._buffer = null;
|
||||
|
||||
// get block coordinates
|
||||
_bx = bx;
|
||||
_by = by;
|
||||
|
||||
// create copy for dynamic implementation
|
||||
_original = new byte[GeoStructure.BLOCK_CELLS * 3];
|
||||
System.arraycopy(_buffer, 0, _original, 0, GeoStructure.BLOCK_CELLS * 3);
|
||||
|
||||
// create list for geo objects
|
||||
_objects = new LinkedList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final short getHeightNearestOriginal(int geoX, int geoY, 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) ((_original[index + 1] & 0x00FF) | (_original[index + 2] << 8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final byte getNsweNearestOriginal(int geoX, int geoY, 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 _original[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getIndexAboveOriginal(int geoX, int geoY, 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 = (_original[index + 1] & 0x00FF) | (_original[index + 2] << 8);
|
||||
|
||||
// check height and return nswe
|
||||
return height > worldZ ? index : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getIndexBelowOriginal(int geoX, int geoY, 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 = (_original[index + 1] & 0x00FF) | (_original[index + 2] << 8);
|
||||
|
||||
// check height and return nswe
|
||||
return height < worldZ ? index : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final short getHeightOriginal(int index)
|
||||
{
|
||||
return (short) ((_original[index + 1] & 0x00FF) | (_original[index + 2] << 8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public final byte getNsweOriginal(int index)
|
||||
{
|
||||
return _original[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized final void addGeoObject(IGeoObject object)
|
||||
{
|
||||
// add geo object, update block geodata when added
|
||||
if (_objects.add(object))
|
||||
{
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized final void removeGeoObject(IGeoObject object)
|
||||
{
|
||||
// remove geo object, update block geodata when removed
|
||||
if (_objects.remove(object))
|
||||
{
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
private final void update()
|
||||
{
|
||||
// copy original geodata, than apply changes
|
||||
System.arraycopy(_original, 0, _buffer, 0, GeoStructure.BLOCK_CELLS * 3);
|
||||
|
||||
// get block geo coordinates
|
||||
final int minBX = _bx * GeoStructure.BLOCK_CELLS_X;
|
||||
final int minBY = _by * GeoStructure.BLOCK_CELLS_Y;
|
||||
final int maxBX = minBX + GeoStructure.BLOCK_CELLS_X;
|
||||
final int maxBY = minBY + GeoStructure.BLOCK_CELLS_Y;
|
||||
|
||||
// for all objects
|
||||
for (IGeoObject object : _objects)
|
||||
{
|
||||
// get object geo coordinates and other object variables
|
||||
final int minOX = object.getGeoX();
|
||||
final int minOY = object.getGeoY();
|
||||
final int minOZ = object.getGeoZ();
|
||||
final int maxOZ = minOZ + object.getHeight();
|
||||
final byte[][] geoData = object.getObjectGeoData();
|
||||
|
||||
// calculate min/max geo coordinates for iteration (intersection of block and object)
|
||||
final int minGX = Math.max(minBX, minOX);
|
||||
final int minGY = Math.max(minBY, minOY);
|
||||
final int maxGX = Math.min(maxBX, minOX + geoData.length);
|
||||
final int maxGY = Math.min(maxBY, minOY + geoData[0].length);
|
||||
|
||||
// iterate over intersection of block and object
|
||||
for (int gx = minGX; gx < maxGX; gx++)
|
||||
{
|
||||
for (int gy = minGY; gy < maxGY; gy++)
|
||||
{
|
||||
// get object nswe
|
||||
final byte objNswe = geoData[gx - minOX][gy - minOY];
|
||||
|
||||
// object contains no change of data in this cell, continue to next cell
|
||||
if (objNswe == 0xFF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// get block index of this cell
|
||||
final int ib = (((gx - minBX) * GeoStructure.BLOCK_CELLS_Y) + (gy - minBY)) * 3;
|
||||
|
||||
// compare block data and original data, when height differs -> height was affected by other geo object
|
||||
// -> cell is inside an object -> no need to check/change it anymore (Z is lifted, nswe is 0)
|
||||
// compare is done in raw format (2 bytes) instead of conversion to short
|
||||
if ((_buffer[ib + 1] != _original[ib + 1]) || (_buffer[ib + 2] != _original[ib + 2]))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// so far cell is not inside of any object
|
||||
if (objNswe == 0)
|
||||
{
|
||||
// cell is inside of this object -> set nswe to 0 and lift Z up
|
||||
|
||||
// set block nswe
|
||||
_buffer[ib] = 0;
|
||||
|
||||
// set block Z to object height
|
||||
_buffer[ib + 1] = (byte) (maxOZ & 0x00FF);
|
||||
_buffer[ib + 2] = (byte) (maxOZ >> 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
// cell is outside of this object -> update nswe
|
||||
|
||||
// height different is too high (trying to update another layer), skip
|
||||
short z = getHeight(ib);
|
||||
if (Math.abs(z - minOZ) > GeoStructure.CELL_IGNORE_HEIGHT)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// adjust block nswe according to the object nswe
|
||||
_buffer[ib] &= objNswe;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General 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 com.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 final boolean hasGeoPos()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final short getHeightNearest(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return _height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final short getHeightNearestOriginal(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return _height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final short getHeightAbove(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
// check and return height
|
||||
return _height > worldZ ? _height : Short.MIN_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final short getHeightBelow(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
// check and return height
|
||||
return _height < worldZ ? _height : Short.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final byte getNsweNearest(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return _nswe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final byte getNsweNearestOriginal(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return _nswe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final byte getNsweAbove(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
// check height and return nswe
|
||||
return _height > worldZ ? _nswe : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final byte getNsweBelow(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
// check height and return nswe
|
||||
return _height < worldZ ? _nswe : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getIndexNearest(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getIndexAbove(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
// check height and return index
|
||||
return _height > worldZ ? 0 : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getIndexAboveOriginal(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return getIndexAbove(geoX, geoY, worldZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getIndexBelow(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
// check height and return index
|
||||
return _height < worldZ ? 0 : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getIndexBelowOriginal(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return getIndexBelow(geoX, geoY, worldZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final short getHeight(int index)
|
||||
{
|
||||
return _height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final short getHeightOriginal(int index)
|
||||
{
|
||||
return _height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final byte getNswe(int index)
|
||||
{
|
||||
return _nswe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final byte getNsweOriginal(int index)
|
||||
{
|
||||
return _nswe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setNswe(int index, byte nswe)
|
||||
{
|
||||
_nswe = nswe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final 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));
|
||||
}
|
||||
}
|
@@ -0,0 +1,466 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General 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 com.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 final 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 final 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
|
||||
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 final boolean hasGeoPos()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final 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 final byte getNswe(int index)
|
||||
{
|
||||
// get nswe
|
||||
return _buffer[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getNsweOriginal(int index)
|
||||
{
|
||||
// get nswe
|
||||
return _buffer[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setNswe(int index, byte nswe)
|
||||
{
|
||||
// set nswe
|
||||
_buffer[index] = nswe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final 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
|
||||
byte layers = _buffer[index++];
|
||||
stream.write(layers);
|
||||
|
||||
// write cell data
|
||||
stream.write(_buffer, index, layers * 3);
|
||||
|
||||
// move index to next cell
|
||||
index += layers * 3;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,312 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General 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 com.l2jmobius.gameserver.geoengine.geodata;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Hasha
|
||||
*/
|
||||
public final class BlockMultilayerDynamic extends BlockMultilayer implements IBlockDynamic
|
||||
{
|
||||
private final int _bx;
|
||||
private final int _by;
|
||||
private final byte[] _original;
|
||||
private final List<IGeoObject> _objects;
|
||||
|
||||
/**
|
||||
* Creates {@link BlockMultilayerDynamic}.
|
||||
* @param bx : Block X coordinate.
|
||||
* @param by : Block Y coordinate.
|
||||
* @param block : The original MultilayerBlock to create a dynamic version from.
|
||||
*/
|
||||
public BlockMultilayerDynamic(int bx, int by, BlockMultilayer block)
|
||||
{
|
||||
// move buffer from ComplexBlock object to this object
|
||||
_buffer = block._buffer;
|
||||
block._buffer = null;
|
||||
|
||||
// get block coordinates
|
||||
_bx = bx;
|
||||
_by = by;
|
||||
|
||||
// create copy for dynamic implementation
|
||||
_original = new byte[_buffer.length];
|
||||
System.arraycopy(_buffer, 0, _original, 0, _buffer.length);
|
||||
|
||||
// create list for geo objects
|
||||
_objects = new LinkedList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public short getHeightNearestOriginal(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
// get cell index
|
||||
final int index = getIndexNearestOriginal(geoX, geoY, worldZ);
|
||||
|
||||
// get height
|
||||
return (short) ((_original[index + 1] & 0x00FF) | (_original[index + 2] << 8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getNsweNearestOriginal(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
// get cell index
|
||||
final int index = getIndexNearestOriginal(geoX, geoY, worldZ);
|
||||
|
||||
// get nswe
|
||||
return _original[index];
|
||||
}
|
||||
|
||||
private final int getIndexNearestOriginal(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 += (_original[index] * 3) + 1;
|
||||
}
|
||||
|
||||
// get layers count and shift to first layer data (first from bottom)
|
||||
byte layers = _original[index++];
|
||||
|
||||
// loop though all cell layers, find closest layer
|
||||
int limit = Integer.MAX_VALUE;
|
||||
while (layers-- > 0)
|
||||
{
|
||||
// get layer height
|
||||
final int height = (_original[index + 1] & 0x00FF) | (_original[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 final int getIndexAboveOriginal(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 += (_original[index] * 3) + 1;
|
||||
}
|
||||
|
||||
// get layers count and shift to last layer data (first from bottom)
|
||||
byte layers = _original[index++];
|
||||
index += (layers - 1) * 3;
|
||||
|
||||
// loop though all layers, find first layer above worldZ
|
||||
while (layers-- > 0)
|
||||
{
|
||||
// get layer height
|
||||
final int height = (_original[index + 1] & 0x00FF) | (_original[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 final int getIndexBelowOriginal(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 += (_original[index] * 3) + 1;
|
||||
}
|
||||
|
||||
// get layers count and shift to first layer data (first from top)
|
||||
byte layers = _original[index++];
|
||||
|
||||
// loop though all layers, find first layer below worldZ
|
||||
while (layers-- > 0)
|
||||
{
|
||||
// get layer height
|
||||
final int height = (_original[index + 1] & 0x00FF) | (_original[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 short getHeightOriginal(int index)
|
||||
{
|
||||
// get height
|
||||
return (short) ((_original[index + 1] & 0x00FF) | (_original[index + 2] << 8));
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte getNsweOriginal(int index)
|
||||
{
|
||||
// get nswe
|
||||
return _original[index];
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized final void addGeoObject(IGeoObject object)
|
||||
{
|
||||
// add geo object, update block geodata when added
|
||||
if (_objects.add(object))
|
||||
{
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized final void removeGeoObject(IGeoObject object)
|
||||
{
|
||||
// remove geo object, update block geodata when removed
|
||||
if (_objects.remove(object))
|
||||
{
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
private final void update()
|
||||
{
|
||||
// copy original geodata, than apply changes
|
||||
System.arraycopy(_original, 0, _buffer, 0, _original.length);
|
||||
|
||||
// get block geo coordinates
|
||||
final int minBX = _bx * GeoStructure.BLOCK_CELLS_X;
|
||||
final int minBY = _by * GeoStructure.BLOCK_CELLS_Y;
|
||||
final int maxBX = minBX + GeoStructure.BLOCK_CELLS_X;
|
||||
final int maxBY = minBY + GeoStructure.BLOCK_CELLS_Y;
|
||||
|
||||
// for all objects
|
||||
for (IGeoObject object : _objects)
|
||||
{
|
||||
// get object geo coordinates and other object variables
|
||||
final int minOX = object.getGeoX();
|
||||
final int minOY = object.getGeoY();
|
||||
final int minOZ = object.getGeoZ();
|
||||
final int maxOZ = minOZ + object.getHeight();
|
||||
final byte[][] geoData = object.getObjectGeoData();
|
||||
|
||||
// calculate min/max geo coordinates for iteration (intersection of block and object)
|
||||
final int minGX = Math.max(minBX, minOX);
|
||||
final int minGY = Math.max(minBY, minOY);
|
||||
final int maxGX = Math.min(maxBX, minOX + geoData.length);
|
||||
final int maxGY = Math.min(maxBY, minOY + geoData[0].length);
|
||||
|
||||
// iterate over intersection of block and object
|
||||
for (int gx = minGX; gx < maxGX; gx++)
|
||||
{
|
||||
for (int gy = minGY; gy < maxGY; gy++)
|
||||
{
|
||||
// get object nswe
|
||||
final byte objNswe = geoData[gx - minOX][gy - minOY];
|
||||
|
||||
// object contains no change of data in this cell, continue to next cell
|
||||
if (objNswe == 0xFF)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// get block index of this cell
|
||||
int ib = getIndexNearest(gx, gy, minOZ);
|
||||
|
||||
// compare block data and original data, when height differs -> height was affected by other geo object
|
||||
// -> cell is inside an object -> no need to check/change it anymore (Z is lifted, nswe is 0)
|
||||
// compare is done in raw format (2 bytes) instead of conversion to short
|
||||
if ((_buffer[ib + 1] != _original[ib + 1]) || (_buffer[ib + 2] != _original[ib + 2]))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// so far cell is not inside of any object
|
||||
if (objNswe == 0)
|
||||
{
|
||||
// cell is inside of this object -> set nswe to 0 and lift Z up
|
||||
|
||||
// set block nswe
|
||||
_buffer[ib] = 0;
|
||||
|
||||
// calculate object height, limit to next layer
|
||||
int z = maxOZ;
|
||||
int i = getIndexAbove(gx, gy, minOZ);
|
||||
if (i != -1)
|
||||
{
|
||||
int az = getHeight(i);
|
||||
if (az <= maxOZ)
|
||||
{
|
||||
z = az - GeoStructure.CELL_IGNORE_HEIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
// set block Z to object height
|
||||
_buffer[ib + 1] = (byte) (z & 0x00FF);
|
||||
_buffer[ib + 2] = (byte) (z >> 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
// cell is outside of this object -> update nswe
|
||||
|
||||
// height different is too high (trying to update another layer), skip
|
||||
short z = getHeight(ib);
|
||||
if (Math.abs(z - minOZ) > GeoStructure.CELL_IGNORE_HEIGHT)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// adjust block nswe according to the object nswe
|
||||
_buffer[ib] &= objNswe;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,150 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.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 final boolean hasGeoPos()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final short getHeightNearest(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return (short) worldZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final short getHeightNearestOriginal(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return (short) worldZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final short getHeightAbove(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return (short) worldZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final short getHeightBelow(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return (short) worldZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final byte getNsweNearest(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return _nswe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final byte getNsweNearestOriginal(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return _nswe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final byte getNsweAbove(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return _nswe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final byte getNsweBelow(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return _nswe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getIndexNearest(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getIndexAbove(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getIndexAboveOriginal(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getIndexBelow(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final int getIndexBelowOriginal(int geoX, int geoY, int worldZ)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final short getHeight(int index)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final short getHeightOriginal(int index)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final byte getNswe(int index)
|
||||
{
|
||||
return _nswe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final byte getNsweOriginal(int index)
|
||||
{
|
||||
return _nswe;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setNswe(int index, byte nswe)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void saveBlock(BufferedOutputStream stream)
|
||||
{
|
||||
}
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.geoengine.geodata;
|
||||
|
||||
/**
|
||||
* @author Hasha
|
||||
*/
|
||||
public enum GeoFormat
|
||||
{
|
||||
L2J("%d_%d.l2j"),
|
||||
L2OFF("%d_%d_conv.dat"),
|
||||
L2D("%d_%d.l2d");
|
||||
|
||||
private final String _filename;
|
||||
|
||||
private GeoFormat(String filename)
|
||||
{
|
||||
_filename = filename;
|
||||
}
|
||||
|
||||
public String getFilename()
|
||||
{
|
||||
return _filename;
|
||||
}
|
||||
}
|
@@ -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 com.l2jmobius.gameserver.geoengine.geodata;
|
||||
|
||||
import com.l2jmobius.gameserver.geoengine.GeoEngine;
|
||||
import com.l2jmobius.gameserver.model.Location;
|
||||
|
||||
/**
|
||||
* @author Hasha
|
||||
*/
|
||||
public class GeoLocation extends Location
|
||||
{
|
||||
private byte _nswe;
|
||||
|
||||
public GeoLocation(int x, int y, int z)
|
||||
{
|
||||
super(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z));
|
||||
_nswe = GeoEngine.getInstance().getNsweNearest(x, y, z);
|
||||
}
|
||||
|
||||
public void set(int x, int y, short z)
|
||||
{
|
||||
super.setXYZ(x, y, GeoEngine.getInstance().getHeightNearest(x, y, z));
|
||||
_nswe = GeoEngine.getInstance().getNsweNearest(x, y, z);
|
||||
}
|
||||
|
||||
public int getGeoX()
|
||||
{
|
||||
return _x;
|
||||
}
|
||||
|
||||
public int getGeoY()
|
||||
{
|
||||
return _y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getX()
|
||||
{
|
||||
return GeoEngine.getInstance().getWorldX(_x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getY()
|
||||
{
|
||||
return GeoEngine.getInstance().getWorldY(_y);
|
||||
}
|
||||
|
||||
public byte getNSWE()
|
||||
{
|
||||
return _nswe;
|
||||
}
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General 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 com.l2jmobius.gameserver.geoengine.geodata;
|
||||
|
||||
import com.l2jmobius.gameserver.model.L2World;
|
||||
|
||||
/**
|
||||
* @author Hasha
|
||||
*/
|
||||
public final 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 byte CELL_FLAG_S_AND_E = CELL_FLAG_S | CELL_FLAG_E;
|
||||
public static final byte CELL_FLAG_S_AND_W = CELL_FLAG_S | CELL_FLAG_W;
|
||||
public static final byte CELL_FLAG_N_AND_E = CELL_FLAG_N | CELL_FLAG_E;
|
||||
public static final byte CELL_FLAG_N_AND_W = CELL_FLAG_N | CELL_FLAG_W;
|
||||
|
||||
public static final int CELL_SIZE = 16;
|
||||
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
|
||||
public static final int GEO_REGIONS_X = ((L2World.TILE_X_MAX - L2World.TILE_X_MIN) + 1);
|
||||
public static final int GEO_REGIONS_Y = ((L2World.TILE_Y_MAX - L2World.TILE_Y_MIN) + 1);
|
||||
|
||||
public static final int GEO_BLOCKS_X = GEO_REGIONS_X * REGION_BLOCKS_X;
|
||||
public static final int GEO_BLOCKS_Y = GEO_REGIONS_Y * REGION_BLOCKS_Y;
|
||||
|
||||
public static final int GEO_CELLS_X = GEO_BLOCKS_X * BLOCK_CELLS_X;
|
||||
public static final int GEO_CELLS_Y = GEO_BLOCKS_Y * BLOCK_CELLS_Y;
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.geoengine.geodata;
|
||||
|
||||
/**
|
||||
* @author Hasha
|
||||
*/
|
||||
public interface IBlockDynamic
|
||||
{
|
||||
/**
|
||||
* Adds {@link IGeoObject} to the {@link ABlock}. The block will update geodata according the object.
|
||||
* @param object : {@link IGeoObject} to be added.
|
||||
*/
|
||||
public void addGeoObject(IGeoObject object);
|
||||
|
||||
/**
|
||||
* Removes {@link IGeoObject} from the {@link ABlock}. The block will update geodata according the object.
|
||||
* @param object : {@link IGeoObject} to be removed.
|
||||
*/
|
||||
public void removeGeoObject(IGeoObject object);
|
||||
}
|
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.geoengine.geodata;
|
||||
|
||||
/**
|
||||
* @author Hasha
|
||||
*/
|
||||
public interface IGeoObject
|
||||
{
|
||||
/**
|
||||
* Returns geodata X coordinate of the {@link IGeoObject}.
|
||||
* @return int : Geodata X coordinate.
|
||||
*/
|
||||
public int getGeoX();
|
||||
|
||||
/**
|
||||
* Returns geodata Y coordinate of the {@link IGeoObject}.
|
||||
* @return int : Geodata Y coordinate.
|
||||
*/
|
||||
public int getGeoY();
|
||||
|
||||
/**
|
||||
* Returns geodata Z coordinate of the {@link IGeoObject}.
|
||||
* @return int : Geodata Z coordinate.
|
||||
*/
|
||||
public int getGeoZ();
|
||||
|
||||
/**
|
||||
* Returns height of the {@link IGeoObject}.
|
||||
* @return int : Height.
|
||||
*/
|
||||
public int getHeight();
|
||||
|
||||
/**
|
||||
* Returns {@link IGeoObject} data.
|
||||
* @return byte[][] : {@link IGeoObject} data.
|
||||
*/
|
||||
public byte[][] getObjectGeoData();
|
||||
}
|
@@ -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 com.l2jmobius.gameserver.geoengine.pathfinding;
|
||||
|
||||
import com.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;
|
||||
}
|
||||
}
|
@@ -0,0 +1,351 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General 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 com.l2jmobius.gameserver.geoengine.pathfinding;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import com.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;
|
||||
|
||||
// pathfinding statistics
|
||||
private long _timeStamp = 0;
|
||||
private long _lastElapsedTime = 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 final Node findPath(int gox, int goy, short goz, int gtx, int gty, short gtz)
|
||||
{
|
||||
// load timestamp
|
||||
_timeStamp = System.currentTimeMillis();
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates list of Nodes to show debug path.
|
||||
* @return List<Node> : nodes
|
||||
*/
|
||||
public final List<Node> debugPath()
|
||||
{
|
||||
List<Node> result = new ArrayList<>();
|
||||
|
||||
for (Node n = _current; n.getParent() != null; n = n.getParent())
|
||||
{
|
||||
result.add(n);
|
||||
n.setCost(-n.getCost());
|
||||
}
|
||||
|
||||
for (Node[] nodes : _buffer)
|
||||
{
|
||||
for (Node node : nodes)
|
||||
{
|
||||
if ((node.getLoc() == null) || (node.getCost() <= 0))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
result.add(node);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public final boolean isLocked()
|
||||
{
|
||||
return _lock.tryLock();
|
||||
}
|
||||
|
||||
public final void free()
|
||||
{
|
||||
_current = null;
|
||||
|
||||
for (Node[] nodes : _buffer)
|
||||
{
|
||||
for (Node node : nodes)
|
||||
{
|
||||
if (node.getLoc() != null)
|
||||
{
|
||||
node.free();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_lock.unlock();
|
||||
_lastElapsedTime = System.currentTimeMillis() - _timeStamp;
|
||||
}
|
||||
|
||||
public final long getElapsedTime()
|
||||
{
|
||||
return _lastElapsedTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check _current Node and add its neighbors to the buffer.
|
||||
*/
|
||||
private final void expand()
|
||||
{
|
||||
// can't move anywhere, don't expand
|
||||
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
|
||||
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
|
||||
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
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user