Merged with released L2J-Unity files.
This commit is contained in:
@ -1,69 +1,69 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.l2jmobius.gameserver.pathfinding.cellnodes;
|
||||
|
||||
import com.l2jmobius.gameserver.pathfinding.AbstractNode;
|
||||
|
||||
public class CellNode extends AbstractNode<NodeLoc>
|
||||
{
|
||||
private CellNode _next = null;
|
||||
private boolean _isInUse = true;
|
||||
private float _cost = -1000;
|
||||
|
||||
public CellNode(NodeLoc loc)
|
||||
{
|
||||
super(loc);
|
||||
}
|
||||
|
||||
public boolean isInUse()
|
||||
{
|
||||
return _isInUse;
|
||||
}
|
||||
|
||||
public void setInUse()
|
||||
{
|
||||
_isInUse = true;
|
||||
}
|
||||
|
||||
public CellNode getNext()
|
||||
{
|
||||
return _next;
|
||||
}
|
||||
|
||||
public void setNext(CellNode next)
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public float getCost()
|
||||
{
|
||||
return _cost;
|
||||
}
|
||||
|
||||
public void setCost(double cost)
|
||||
{
|
||||
_cost = (float) cost;
|
||||
}
|
||||
|
||||
public void free()
|
||||
{
|
||||
setParent(null);
|
||||
_cost = -1000;
|
||||
_isInUse = false;
|
||||
_next = null;
|
||||
}
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General 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.pathfinding.cellnodes;
|
||||
|
||||
import com.l2jmobius.gameserver.pathfinding.AbstractNode;
|
||||
|
||||
public class CellNode extends AbstractNode<NodeLoc>
|
||||
{
|
||||
private CellNode _next = null;
|
||||
private boolean _isInUse = true;
|
||||
private float _cost = -1000;
|
||||
|
||||
public CellNode(NodeLoc loc)
|
||||
{
|
||||
super(loc);
|
||||
}
|
||||
|
||||
public boolean isInUse()
|
||||
{
|
||||
return _isInUse;
|
||||
}
|
||||
|
||||
public void setInUse()
|
||||
{
|
||||
_isInUse = true;
|
||||
}
|
||||
|
||||
public CellNode getNext()
|
||||
{
|
||||
return _next;
|
||||
}
|
||||
|
||||
public void setNext(CellNode next)
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
|
||||
public float getCost()
|
||||
{
|
||||
return _cost;
|
||||
}
|
||||
|
||||
public void setCost(double cost)
|
||||
{
|
||||
_cost = (float) cost;
|
||||
}
|
||||
|
||||
public void free()
|
||||
{
|
||||
setParent(null);
|
||||
_cost = -1000;
|
||||
_isInUse = false;
|
||||
_next = null;
|
||||
}
|
||||
}
|
@ -1,335 +1,364 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General 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.pathfinding.cellnodes;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
|
||||
/**
|
||||
* @author DS Credits to Diamond
|
||||
*/
|
||||
public class CellNodeBuffer
|
||||
{
|
||||
private static final int MAX_ITERATIONS = 3500;
|
||||
|
||||
private final ReentrantLock _lock = new ReentrantLock();
|
||||
private final int _mapSize;
|
||||
private final CellNode[][] _buffer;
|
||||
|
||||
private int _baseX = 0;
|
||||
private int _baseY = 0;
|
||||
|
||||
private int _targetX = 0;
|
||||
private int _targetY = 0;
|
||||
private int _targetZ = 0;
|
||||
|
||||
private long _timeStamp = 0;
|
||||
private long _lastElapsedTime = 0;
|
||||
|
||||
private CellNode _current = null;
|
||||
|
||||
public CellNodeBuffer(int size)
|
||||
{
|
||||
_mapSize = size;
|
||||
_buffer = new CellNode[_mapSize][_mapSize];
|
||||
}
|
||||
|
||||
public final boolean lock()
|
||||
{
|
||||
return _lock.tryLock();
|
||||
}
|
||||
|
||||
public final CellNode findPath(int x, int y, int z, int tx, int ty, int tz)
|
||||
{
|
||||
_timeStamp = System.currentTimeMillis();
|
||||
_baseX = x + ((tx - x - _mapSize) / 2); // middle of the line (x,y) - (tx,ty)
|
||||
_baseY = y + ((ty - y - _mapSize) / 2); // will be in the center of the buffer
|
||||
_targetX = tx;
|
||||
_targetY = ty;
|
||||
_targetZ = tz;
|
||||
_current = getNode(x, y, z);
|
||||
_current.setCost(getCost(x, y, z, Config.HIGH_WEIGHT));
|
||||
|
||||
for (int count = 0; count < MAX_ITERATIONS; count++)
|
||||
{
|
||||
if ((_current.getLoc().getNodeX() == _targetX) && (_current.getLoc().getNodeY() == _targetY) && (Math.abs(_current.getLoc().getZ() - _targetZ) < 64))
|
||||
{
|
||||
return _current; // found
|
||||
}
|
||||
|
||||
getNeighbors();
|
||||
if (_current.getNext() == null)
|
||||
{
|
||||
return null; // no more ways
|
||||
}
|
||||
|
||||
_current = _current.getNext();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public final void free()
|
||||
{
|
||||
_current = null;
|
||||
|
||||
CellNode node;
|
||||
for (int i = 0; i < _mapSize; i++)
|
||||
{
|
||||
for (int j = 0; j < _mapSize; j++)
|
||||
{
|
||||
node = _buffer[i][j];
|
||||
if (node != null)
|
||||
{
|
||||
node.free();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_lock.unlock();
|
||||
_lastElapsedTime = System.currentTimeMillis() - _timeStamp;
|
||||
}
|
||||
|
||||
public final long getElapsedTime()
|
||||
{
|
||||
return _lastElapsedTime;
|
||||
}
|
||||
|
||||
public final List<CellNode> debugPath()
|
||||
{
|
||||
final List<CellNode> result = new LinkedList<>();
|
||||
for (CellNode n = _current; n.getParent() != null; n = (CellNode) n.getParent())
|
||||
{
|
||||
result.add(n);
|
||||
n.setCost(-n.getCost());
|
||||
}
|
||||
|
||||
for (int i = 0; i < _mapSize; i++)
|
||||
{
|
||||
for (int j = 0; j < _mapSize; j++)
|
||||
{
|
||||
final CellNode n = _buffer[i][j];
|
||||
if ((n == null) || !n.isInUse() || (n.getCost() <= 0))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
result.add(n);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private final void getNeighbors()
|
||||
{
|
||||
if (_current.getLoc().canGoNone())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
final int x = _current.getLoc().getNodeX();
|
||||
final int y = _current.getLoc().getNodeY();
|
||||
final int z = _current.getLoc().getZ();
|
||||
|
||||
CellNode nodeE = null;
|
||||
CellNode nodeS = null;
|
||||
CellNode nodeW = null;
|
||||
CellNode nodeN = null;
|
||||
|
||||
// East
|
||||
if (_current.getLoc().canGoEast())
|
||||
{
|
||||
nodeE = addNode(x + 1, y, z, false);
|
||||
}
|
||||
|
||||
// South
|
||||
if (_current.getLoc().canGoSouth())
|
||||
{
|
||||
nodeS = addNode(x, y + 1, z, false);
|
||||
}
|
||||
|
||||
// West
|
||||
if (_current.getLoc().canGoWest())
|
||||
{
|
||||
nodeW = addNode(x - 1, y, z, false);
|
||||
}
|
||||
|
||||
// North
|
||||
if (_current.getLoc().canGoNorth())
|
||||
{
|
||||
nodeN = addNode(x, y - 1, z, false);
|
||||
}
|
||||
|
||||
if (!Config.ADVANCED_DIAGONAL_STRATEGY)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// SouthEast
|
||||
if ((nodeE != null) && (nodeS != null) && nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast())
|
||||
{
|
||||
addNode(x + 1, y + 1, z, true);
|
||||
}
|
||||
|
||||
// SouthWest
|
||||
if ((nodeS != null) && (nodeW != null) && nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest())
|
||||
{
|
||||
addNode(x - 1, y + 1, z, true);
|
||||
}
|
||||
|
||||
// NorthEast
|
||||
if ((nodeN != null) && (nodeE != null) && nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast())
|
||||
{
|
||||
addNode(x + 1, y - 1, z, true);
|
||||
}
|
||||
|
||||
// NorthWest
|
||||
if ((nodeN != null) && (nodeW != null) && nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest())
|
||||
{
|
||||
addNode(x - 1, y - 1, z, true);
|
||||
}
|
||||
}
|
||||
|
||||
private final CellNode getNode(int x, int y, int z)
|
||||
{
|
||||
final int aX = x - _baseX;
|
||||
if ((aX < 0) || (aX >= _mapSize))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
final int aY = y - _baseY;
|
||||
if ((aY < 0) || (aY >= _mapSize))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
CellNode result = _buffer[aX][aY];
|
||||
if (result == null)
|
||||
{
|
||||
result = new CellNode(new NodeLoc(x, y, z));
|
||||
_buffer[aX][aY] = result;
|
||||
}
|
||||
else if (!result.isInUse())
|
||||
{
|
||||
result.setInUse();
|
||||
// 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 final CellNode addNode(int x, int y, int z, boolean diagonal)
|
||||
{
|
||||
final CellNode newNode = getNode(x, y, z);
|
||||
if (newNode == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (newNode.getCost() >= 0)
|
||||
{
|
||||
return newNode;
|
||||
}
|
||||
|
||||
final int geoZ = newNode.getLoc().getZ();
|
||||
|
||||
final int stepZ = Math.abs(geoZ - _current.getLoc().getZ());
|
||||
float weight = diagonal ? Config.DIAGONAL_WEIGHT : Config.LOW_WEIGHT;
|
||||
|
||||
if (!newNode.getLoc().canGoAll() || (stepZ > 16))
|
||||
{
|
||||
weight = Config.HIGH_WEIGHT;
|
||||
}
|
||||
else if (isHighWeight(x + 1, y, geoZ))
|
||||
{
|
||||
weight = Config.MEDIUM_WEIGHT;
|
||||
}
|
||||
else if (isHighWeight(x - 1, y, geoZ))
|
||||
{
|
||||
weight = Config.MEDIUM_WEIGHT;
|
||||
}
|
||||
else if (isHighWeight(x, y + 1, geoZ))
|
||||
{
|
||||
weight = Config.MEDIUM_WEIGHT;
|
||||
}
|
||||
else if (isHighWeight(x, y - 1, geoZ))
|
||||
{
|
||||
weight = Config.MEDIUM_WEIGHT;
|
||||
}
|
||||
|
||||
newNode.setParent(_current);
|
||||
newNode.setCost(getCost(x, y, geoZ, weight));
|
||||
|
||||
CellNode node = _current;
|
||||
int count = 0;
|
||||
while ((node.getNext() != null) && (count < (MAX_ITERATIONS * 4)))
|
||||
{
|
||||
count++;
|
||||
if (node.getNext().getCost() > newNode.getCost())
|
||||
{
|
||||
// insert node into a chain
|
||||
newNode.setNext(node.getNext());
|
||||
break;
|
||||
}
|
||||
node = node.getNext();
|
||||
}
|
||||
if (count == (MAX_ITERATIONS * 4))
|
||||
{
|
||||
System.err.println("Pathfinding: too long loop detected, cost:" + newNode.getCost());
|
||||
}
|
||||
|
||||
node.setNext(newNode); // add last
|
||||
|
||||
return newNode;
|
||||
}
|
||||
|
||||
private final boolean isHighWeight(int x, int y, int z)
|
||||
{
|
||||
final CellNode result = getNode(x, y, z);
|
||||
return (result == null) || !result.getLoc().canGoAll() || (Math.abs(result.getLoc().getZ() - z) > 16);
|
||||
}
|
||||
|
||||
private final double getCost(int x, int y, int z, float weight)
|
||||
{
|
||||
final int dX = x - _targetX;
|
||||
final int dY = y - _targetY;
|
||||
final int dZ = z - _targetZ;
|
||||
// Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16
|
||||
double result = Math.sqrt((dX * dX) + (dY * dY) + ((dZ * dZ) / 256.0));
|
||||
if (result > weight)
|
||||
{
|
||||
result += weight;
|
||||
}
|
||||
|
||||
if (result > Float.MAX_VALUE)
|
||||
{
|
||||
result = Float.MAX_VALUE;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General 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.pathfinding.cellnodes;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
|
||||
/**
|
||||
* @author DS Credits to Diamond
|
||||
*/
|
||||
public class CellNodeBuffer
|
||||
{
|
||||
private static final int MAX_ITERATIONS = 3500;
|
||||
|
||||
private final ReentrantLock _lock = new ReentrantLock();
|
||||
private final int _mapSize;
|
||||
private final CellNode[][] _buffer;
|
||||
|
||||
private int _baseX = 0;
|
||||
private int _baseY = 0;
|
||||
|
||||
private int _targetX = 0;
|
||||
private int _targetY = 0;
|
||||
private int _targetZ = 0;
|
||||
|
||||
private long _timeStamp = 0;
|
||||
private long _lastElapsedTime = 0;
|
||||
|
||||
private CellNode _current = null;
|
||||
|
||||
public CellNodeBuffer(int size)
|
||||
{
|
||||
_mapSize = size;
|
||||
_buffer = new CellNode[_mapSize][_mapSize];
|
||||
}
|
||||
|
||||
public final boolean lock()
|
||||
{
|
||||
return _lock.tryLock();
|
||||
}
|
||||
|
||||
public final CellNode findPath(int x, int y, int z, int tx, int ty, int tz)
|
||||
{
|
||||
_timeStamp = System.currentTimeMillis();
|
||||
_baseX = x + ((tx - x - _mapSize) / 2); // middle of the line (x,y) - (tx,ty)
|
||||
_baseY = y + ((ty - y - _mapSize) / 2); // will be in the center of the buffer
|
||||
_targetX = tx;
|
||||
_targetY = ty;
|
||||
_targetZ = tz;
|
||||
_current = getNode(x, y, z);
|
||||
_current.setCost(getCost(x, y, z, Config.HIGH_WEIGHT));
|
||||
|
||||
for (int count = 0; count < MAX_ITERATIONS; count++)
|
||||
{
|
||||
if ((_current.getLoc().getNodeX() == _targetX) && (_current.getLoc().getNodeY() == _targetY) && (Math.abs(_current.getLoc().getZ() - _targetZ) < 64))
|
||||
{
|
||||
return _current; // found
|
||||
}
|
||||
|
||||
getNeighbors();
|
||||
if (_current.getNext() == null)
|
||||
{
|
||||
return null; // no more ways
|
||||
}
|
||||
|
||||
_current = _current.getNext();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public final void free()
|
||||
{
|
||||
_current = null;
|
||||
|
||||
CellNode node;
|
||||
for (int i = 0; i < _mapSize; i++)
|
||||
{
|
||||
for (int j = 0; j < _mapSize; j++)
|
||||
{
|
||||
node = _buffer[i][j];
|
||||
if (node != null)
|
||||
{
|
||||
node.free();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_lock.unlock();
|
||||
_lastElapsedTime = System.currentTimeMillis() - _timeStamp;
|
||||
}
|
||||
|
||||
public final long getElapsedTime()
|
||||
{
|
||||
return _lastElapsedTime;
|
||||
}
|
||||
|
||||
public final List<CellNode> debugPath()
|
||||
{
|
||||
final List<CellNode> result = new LinkedList<>();
|
||||
|
||||
for (CellNode n = _current; n.getParent() != null; n = (CellNode) n.getParent())
|
||||
{
|
||||
result.add(n);
|
||||
n.setCost(-n.getCost());
|
||||
}
|
||||
|
||||
for (int i = 0; i < _mapSize; i++)
|
||||
{
|
||||
for (int j = 0; j < _mapSize; j++)
|
||||
{
|
||||
final CellNode n = _buffer[i][j];
|
||||
if ((n == null) || !n.isInUse() || (n.getCost() <= 0))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
result.add(n);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private 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);
|
||||
}
|
||||
|
||||
if (Config.ADVANCED_DIAGONAL_STRATEGY)
|
||||
{
|
||||
// SouthEast
|
||||
if ((nodeE != null) && (nodeS != null))
|
||||
{
|
||||
if (nodeE.getLoc().canGoSouth() && nodeS.getLoc().canGoEast())
|
||||
{
|
||||
addNode(x + 1, y + 1, z, true);
|
||||
}
|
||||
}
|
||||
|
||||
// SouthWest
|
||||
if ((nodeS != null) && (nodeW != null))
|
||||
{
|
||||
if (nodeW.getLoc().canGoSouth() && nodeS.getLoc().canGoWest())
|
||||
{
|
||||
addNode(x - 1, y + 1, z, true);
|
||||
}
|
||||
}
|
||||
|
||||
// NorthEast
|
||||
if ((nodeN != null) && (nodeE != null))
|
||||
{
|
||||
if (nodeE.getLoc().canGoNorth() && nodeN.getLoc().canGoEast())
|
||||
{
|
||||
addNode(x + 1, y - 1, z, true);
|
||||
}
|
||||
}
|
||||
|
||||
// NorthWest
|
||||
if ((nodeN != null) && (nodeW != null))
|
||||
{
|
||||
if (nodeW.getLoc().canGoNorth() && nodeN.getLoc().canGoWest())
|
||||
{
|
||||
addNode(x - 1, y - 1, z, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CellNode getNode(int x, int y, int z)
|
||||
{
|
||||
final int aX = x - _baseX;
|
||||
if ((aX < 0) || (aX >= _mapSize))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
final int aY = y - _baseY;
|
||||
if ((aY < 0) || (aY >= _mapSize))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
CellNode result = _buffer[aX][aY];
|
||||
if (result == null)
|
||||
{
|
||||
result = new CellNode(new NodeLoc(x, y, z));
|
||||
_buffer[aX][aY] = result;
|
||||
}
|
||||
else if (!result.isInUse())
|
||||
{
|
||||
result.setInUse();
|
||||
// reinit node if needed
|
||||
if (result.getLoc() != null)
|
||||
{
|
||||
result.getLoc().set(x, y, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.setLoc(new NodeLoc(x, y, z));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private CellNode addNode(int x, int y, int z, boolean diagonal)
|
||||
{
|
||||
final CellNode newNode = getNode(x, y, z);
|
||||
if (newNode == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (newNode.getCost() >= 0)
|
||||
{
|
||||
return newNode;
|
||||
}
|
||||
|
||||
final int geoZ = newNode.getLoc().getZ();
|
||||
|
||||
final int stepZ = Math.abs(geoZ - _current.getLoc().getZ());
|
||||
float weight = diagonal ? Config.DIAGONAL_WEIGHT : Config.LOW_WEIGHT;
|
||||
|
||||
if (!newNode.getLoc().canGoAll() || (stepZ > 16))
|
||||
{
|
||||
weight = Config.HIGH_WEIGHT;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isHighWeight(x + 1, y, geoZ))
|
||||
{
|
||||
weight = Config.MEDIUM_WEIGHT;
|
||||
}
|
||||
else if (isHighWeight(x - 1, y, geoZ))
|
||||
{
|
||||
weight = Config.MEDIUM_WEIGHT;
|
||||
}
|
||||
else if (isHighWeight(x, y + 1, geoZ))
|
||||
{
|
||||
weight = Config.MEDIUM_WEIGHT;
|
||||
}
|
||||
else if (isHighWeight(x, y - 1, geoZ))
|
||||
{
|
||||
weight = Config.MEDIUM_WEIGHT;
|
||||
}
|
||||
}
|
||||
|
||||
newNode.setParent(_current);
|
||||
newNode.setCost(getCost(x, y, geoZ, weight));
|
||||
|
||||
CellNode node = _current;
|
||||
int count = 0;
|
||||
while ((node.getNext() != null) && (count < (MAX_ITERATIONS * 4)))
|
||||
{
|
||||
count++;
|
||||
if (node.getNext().getCost() > newNode.getCost())
|
||||
{
|
||||
// insert node into a chain
|
||||
newNode.setNext(node.getNext());
|
||||
break;
|
||||
}
|
||||
node = node.getNext();
|
||||
}
|
||||
if (count == (MAX_ITERATIONS * 4))
|
||||
{
|
||||
System.err.println("Pathfinding: too long loop detected, cost:" + newNode.getCost());
|
||||
}
|
||||
|
||||
node.setNext(newNode); // add last
|
||||
|
||||
return newNode;
|
||||
}
|
||||
|
||||
private boolean isHighWeight(int x, int y, int z)
|
||||
{
|
||||
final CellNode result = getNode(x, y, z);
|
||||
if (result == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!result.getLoc().canGoAll())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (Math.abs(result.getLoc().getZ() - z) > 16)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private double getCost(int x, int y, int z, float weight)
|
||||
{
|
||||
final int dX = x - _targetX;
|
||||
final int dY = y - _targetY;
|
||||
final int dZ = z - _targetZ;
|
||||
// Math.abs(dx) + Math.abs(dy) + Math.abs(dz) / 16
|
||||
double result = Math.sqrt((dX * dX) + (dY * dY) + ((dZ * dZ) / 256.0));
|
||||
if (result > weight)
|
||||
{
|
||||
result += weight;
|
||||
}
|
||||
|
||||
if (result > Float.MAX_VALUE)
|
||||
{
|
||||
result = Float.MAX_VALUE;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
@ -1,400 +1,440 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General 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.pathfinding.cellnodes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import com.l2jmobius.gameserver.GeoData;
|
||||
import com.l2jmobius.gameserver.idfactory.IdFactory;
|
||||
import com.l2jmobius.gameserver.model.itemcontainer.Inventory;
|
||||
import com.l2jmobius.gameserver.model.items.instance.L2ItemInstance;
|
||||
import com.l2jmobius.gameserver.pathfinding.AbstractNode;
|
||||
import com.l2jmobius.gameserver.pathfinding.AbstractNodeLoc;
|
||||
import com.l2jmobius.gameserver.pathfinding.PathFinding;
|
||||
import com.l2jmobius.util.StringUtil;
|
||||
|
||||
/**
|
||||
* @author Sami, DS Credits to Diamond
|
||||
*/
|
||||
public class CellPathFinding extends PathFinding
|
||||
{
|
||||
private static final Logger _log = Logger.getLogger(CellPathFinding.class.getName());
|
||||
private BufferInfo[] _allBuffers;
|
||||
private int _findSuccess = 0;
|
||||
private int _findFails = 0;
|
||||
private int _postFilterUses = 0;
|
||||
private int _postFilterPlayableUses = 0;
|
||||
private int _postFilterPasses = 0;
|
||||
private long _postFilterElapsed = 0;
|
||||
|
||||
private List<L2ItemInstance> _debugItems = null;
|
||||
|
||||
public static CellPathFinding getInstance()
|
||||
{
|
||||
return SingletonHolder._instance;
|
||||
}
|
||||
|
||||
protected CellPathFinding()
|
||||
{
|
||||
try
|
||||
{
|
||||
final String[] array = Config.PATHFIND_BUFFERS.split(";");
|
||||
|
||||
_allBuffers = new BufferInfo[array.length];
|
||||
|
||||
String buf;
|
||||
String[] args;
|
||||
for (int i = 0; i < array.length; i++)
|
||||
{
|
||||
buf = array[i];
|
||||
args = buf.split("x");
|
||||
if (args.length != 2)
|
||||
{
|
||||
throw new Exception("Invalid buffer definition: " + buf);
|
||||
}
|
||||
|
||||
_allBuffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1]));
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e);
|
||||
throw new Error("CellPathFinding: load aborted");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pathNodesExist(short regionoffset)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AbstractNodeLoc> findPath(int x, int y, int z, int tx, int ty, int tz, int instanceId, boolean playable)
|
||||
{
|
||||
final int gx = GeoData.getInstance().getGeoX(x);
|
||||
final int gy = GeoData.getInstance().getGeoY(y);
|
||||
if (!GeoData.getInstance().hasGeo(x, y))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
final int gz = GeoData.getInstance().getHeight(x, y, z);
|
||||
final int gtx = GeoData.getInstance().getGeoX(tx);
|
||||
final int gty = GeoData.getInstance().getGeoY(ty);
|
||||
if (!GeoData.getInstance().hasGeo(tx, ty))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
final int gtz = GeoData.getInstance().getHeight(tx, ty, tz);
|
||||
final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty))), playable);
|
||||
if (buffer == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
final boolean debug = playable && Config.DEBUG_PATH;
|
||||
|
||||
if (debug)
|
||||
{
|
||||
if (_debugItems == null)
|
||||
{
|
||||
_debugItems = new CopyOnWriteArrayList<>();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (L2ItemInstance item : _debugItems)
|
||||
{
|
||||
item.decayMe();
|
||||
}
|
||||
|
||||
_debugItems.clear();
|
||||
}
|
||||
}
|
||||
|
||||
List<AbstractNodeLoc> path = null;
|
||||
try
|
||||
{
|
||||
final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz);
|
||||
|
||||
if (debug)
|
||||
{
|
||||
for (CellNode n : buffer.debugPath())
|
||||
{
|
||||
if (n.getCost() < 0)
|
||||
{
|
||||
dropDebugItem(1831, (int) (-n.getCost() * 10), n.getLoc());
|
||||
}
|
||||
else
|
||||
{
|
||||
// known nodes
|
||||
dropDebugItem(Inventory.ADENA_ID, (int) (n.getCost() * 10), n.getLoc());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
_findFails++;
|
||||
return null;
|
||||
}
|
||||
|
||||
path = constructPath(result);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.log(Level.WARNING, "", e);
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
buffer.free();
|
||||
}
|
||||
|
||||
if ((path.size() < 3) || (Config.MAX_POSTFILTER_PASSES <= 0))
|
||||
{
|
||||
_findSuccess++;
|
||||
return path;
|
||||
}
|
||||
|
||||
final long timeStamp = System.currentTimeMillis();
|
||||
_postFilterUses++;
|
||||
if (playable)
|
||||
{
|
||||
_postFilterPlayableUses++;
|
||||
}
|
||||
|
||||
boolean remove;
|
||||
int pass = 0;
|
||||
do
|
||||
{
|
||||
pass++;
|
||||
_postFilterPasses++;
|
||||
|
||||
remove = false;
|
||||
final Iterator<AbstractNodeLoc> endPoint = path.iterator();
|
||||
endPoint.next();
|
||||
int currentX = x;
|
||||
int currentY = y;
|
||||
int currentZ = z;
|
||||
|
||||
int midPoint = 0;
|
||||
while (endPoint.hasNext())
|
||||
{
|
||||
final AbstractNodeLoc locMiddle = path.get(midPoint);
|
||||
final AbstractNodeLoc locEnd = endPoint.next();
|
||||
if (GeoData.getInstance().canMove(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instanceId))
|
||||
{
|
||||
path.remove(midPoint);
|
||||
remove = true;
|
||||
if (debug)
|
||||
{
|
||||
dropDebugItem(735, 1, locMiddle);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
currentX = locMiddle.getX();
|
||||
currentY = locMiddle.getY();
|
||||
currentZ = locMiddle.getZ();
|
||||
midPoint++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// only one postfilter pass for AI
|
||||
while (playable && remove && (path.size() > 2) && (pass < Config.MAX_POSTFILTER_PASSES));
|
||||
|
||||
if (debug)
|
||||
{
|
||||
path.forEach(n -> dropDebugItem(65, 1, n));
|
||||
}
|
||||
|
||||
_findSuccess++;
|
||||
_postFilterElapsed += System.currentTimeMillis() - timeStamp;
|
||||
return path;
|
||||
}
|
||||
|
||||
private List<AbstractNodeLoc> constructPath(AbstractNode<NodeLoc> node)
|
||||
{
|
||||
final List<AbstractNodeLoc> path = new CopyOnWriteArrayList<>();
|
||||
int previousDirectionX = Integer.MIN_VALUE;
|
||||
int previousDirectionY = Integer.MIN_VALUE;
|
||||
int directionX, directionY;
|
||||
|
||||
while (node.getParent() != null)
|
||||
{
|
||||
if (!Config.ADVANCED_DIAGONAL_STRATEGY && (node.getParent().getParent() != null))
|
||||
{
|
||||
final int tmpX = node.getLoc().getNodeX() - node.getParent().getParent().getLoc().getNodeX();
|
||||
final int tmpY = node.getLoc().getNodeY() - node.getParent().getParent().getLoc().getNodeY();
|
||||
if (Math.abs(tmpX) == Math.abs(tmpY))
|
||||
{
|
||||
directionX = tmpX;
|
||||
directionY = tmpY;
|
||||
}
|
||||
else
|
||||
{
|
||||
directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX();
|
||||
directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX();
|
||||
directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY();
|
||||
}
|
||||
|
||||
// only add a new route point if moving direction changes
|
||||
if ((directionX != previousDirectionX) || (directionY != previousDirectionY))
|
||||
{
|
||||
previousDirectionX = directionX;
|
||||
previousDirectionY = directionY;
|
||||
|
||||
path.add(0, node.getLoc());
|
||||
node.setLoc(null);
|
||||
}
|
||||
|
||||
node = node.getParent();
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
private final CellNodeBuffer alloc(int size, boolean playable)
|
||||
{
|
||||
CellNodeBuffer current = null;
|
||||
for (BufferInfo i : _allBuffers)
|
||||
{
|
||||
if (i.mapSize >= size)
|
||||
{
|
||||
for (CellNodeBuffer buf : i.bufs)
|
||||
{
|
||||
if (buf.lock())
|
||||
{
|
||||
i.uses++;
|
||||
if (playable)
|
||||
{
|
||||
i.playableUses++;
|
||||
}
|
||||
i.elapsed += buf.getElapsedTime();
|
||||
current = buf;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (current != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// not found, allocate temporary buffer
|
||||
current = new CellNodeBuffer(i.mapSize);
|
||||
current.lock();
|
||||
if (i.bufs.size() < i.count)
|
||||
{
|
||||
i.bufs.add(current);
|
||||
i.uses++;
|
||||
if (playable)
|
||||
{
|
||||
i.playableUses++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
i.overflows++;
|
||||
if (playable)
|
||||
{
|
||||
i.playableOverflows++;
|
||||
// System.err.println("Overflow, size requested: " + size + " playable:"+playable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
private final void dropDebugItem(int itemId, int num, AbstractNodeLoc loc)
|
||||
{
|
||||
final L2ItemInstance item = new L2ItemInstance(IdFactory.getInstance().getNextId(), itemId);
|
||||
item.setCount(num);
|
||||
item.spawnMe(loc.getX(), loc.getY(), loc.getZ());
|
||||
_debugItems.add(item);
|
||||
}
|
||||
|
||||
private static final class BufferInfo
|
||||
{
|
||||
final int mapSize;
|
||||
final int count;
|
||||
List<CellNodeBuffer> bufs;
|
||||
int uses = 0;
|
||||
int playableUses = 0;
|
||||
int overflows = 0;
|
||||
int playableOverflows = 0;
|
||||
long elapsed = 0;
|
||||
|
||||
public BufferInfo(int size, int cnt)
|
||||
{
|
||||
mapSize = size;
|
||||
count = cnt;
|
||||
bufs = new ArrayList<>(count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
final StringBuilder stat = new StringBuilder(100);
|
||||
StringUtil.append(stat, String.valueOf(mapSize), "x", String.valueOf(mapSize), " num:", String.valueOf(bufs.size()), "/", String.valueOf(count), " uses:", String.valueOf(uses), "/", String.valueOf(playableUses));
|
||||
if (uses > 0)
|
||||
{
|
||||
StringUtil.append(stat, " total/avg(ms):", String.valueOf(elapsed), "/", String.format("%1.2f", (double) elapsed / uses));
|
||||
}
|
||||
|
||||
StringUtil.append(stat, " ovf:", String.valueOf(overflows), "/", String.valueOf(playableOverflows));
|
||||
|
||||
return stat.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getStat()
|
||||
{
|
||||
final String[] result = new String[_allBuffers.length + 1];
|
||||
for (int i = 0; i < _allBuffers.length; i++)
|
||||
{
|
||||
result[i] = _allBuffers[i].toString();
|
||||
}
|
||||
|
||||
final StringBuilder stat = new StringBuilder(100);
|
||||
StringUtil.append(stat, "LOS postfilter uses:", String.valueOf(_postFilterUses), "/", String.valueOf(_postFilterPlayableUses));
|
||||
if (_postFilterUses > 0)
|
||||
{
|
||||
StringUtil.append(stat, " total/avg(ms):", String.valueOf(_postFilterElapsed), "/", String.format("%1.2f", (double) _postFilterElapsed / _postFilterUses), " passes total/avg:", String.valueOf(_postFilterPasses), "/", String.format("%1.1f", (double) _postFilterPasses / _postFilterUses), Config.EOL);
|
||||
}
|
||||
StringUtil.append(stat, "Pathfind success/fail:", String.valueOf(_findSuccess), "/", String.valueOf(_findFails));
|
||||
result[result.length - 1] = stat.toString();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static class SingletonHolder
|
||||
{
|
||||
protected static final CellPathFinding _instance = new CellPathFinding();
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General 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.pathfinding.cellnodes;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import com.l2jmobius.gameserver.GeoData;
|
||||
import com.l2jmobius.gameserver.idfactory.IdFactory;
|
||||
import com.l2jmobius.gameserver.model.instancezone.Instance;
|
||||
import com.l2jmobius.gameserver.model.itemcontainer.Inventory;
|
||||
import com.l2jmobius.gameserver.model.items.instance.L2ItemInstance;
|
||||
import com.l2jmobius.gameserver.pathfinding.AbstractNode;
|
||||
import com.l2jmobius.gameserver.pathfinding.AbstractNodeLoc;
|
||||
import com.l2jmobius.gameserver.pathfinding.PathFinding;
|
||||
|
||||
/**
|
||||
* @author Sami, DS Credits to Diamond
|
||||
*/
|
||||
public class CellPathFinding extends PathFinding
|
||||
{
|
||||
private static final Logger _log = Logger.getLogger(CellPathFinding.class.getName());
|
||||
private BufferInfo[] _allBuffers;
|
||||
private int _findSuccess = 0;
|
||||
private int _findFails = 0;
|
||||
private int _postFilterUses = 0;
|
||||
private int _postFilterPlayableUses = 0;
|
||||
private int _postFilterPasses = 0;
|
||||
private long _postFilterElapsed = 0;
|
||||
|
||||
private List<L2ItemInstance> _debugItems = null;
|
||||
|
||||
public static CellPathFinding getInstance()
|
||||
{
|
||||
return SingletonHolder._instance;
|
||||
}
|
||||
|
||||
protected CellPathFinding()
|
||||
{
|
||||
try
|
||||
{
|
||||
final String[] array = Config.PATHFIND_BUFFERS.split(";");
|
||||
|
||||
_allBuffers = new BufferInfo[array.length];
|
||||
|
||||
String buf;
|
||||
String[] args;
|
||||
for (int i = 0; i < array.length; i++)
|
||||
{
|
||||
buf = array[i];
|
||||
args = buf.split("x");
|
||||
if (args.length != 2)
|
||||
{
|
||||
throw new Exception("Invalid buffer definition: " + buf);
|
||||
}
|
||||
|
||||
_allBuffers[i] = new BufferInfo(Integer.parseInt(args[0]), Integer.parseInt(args[1]));
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.log(Level.WARNING, "CellPathFinding: Problem during buffer init: " + e.getMessage(), e);
|
||||
throw new Error("CellPathFinding: load aborted");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pathNodesExist(short regionoffset)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AbstractNodeLoc> findPath(int x, int y, int z, int tx, int ty, int tz, Instance instance, boolean playable)
|
||||
{
|
||||
final int gx = GeoData.getInstance().getGeoX(x);
|
||||
final int gy = GeoData.getInstance().getGeoY(y);
|
||||
if (!GeoData.getInstance().hasGeo(x, y))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
final int gz = GeoData.getInstance().getHeight(x, y, z);
|
||||
final int gtx = GeoData.getInstance().getGeoX(tx);
|
||||
final int gty = GeoData.getInstance().getGeoY(ty);
|
||||
if (!GeoData.getInstance().hasGeo(tx, ty))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
final int gtz = GeoData.getInstance().getHeight(tx, ty, tz);
|
||||
final CellNodeBuffer buffer = alloc(64 + (2 * Math.max(Math.abs(gx - gtx), Math.abs(gy - gty))), playable);
|
||||
if (buffer == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
final boolean debug = playable && Config.DEBUG_PATH;
|
||||
|
||||
if (debug)
|
||||
{
|
||||
if (_debugItems == null)
|
||||
{
|
||||
_debugItems = new CopyOnWriteArrayList<>();
|
||||
}
|
||||
else
|
||||
{
|
||||
for (L2ItemInstance item : _debugItems)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
item.decayMe();
|
||||
}
|
||||
|
||||
_debugItems.clear();
|
||||
}
|
||||
}
|
||||
|
||||
List<AbstractNodeLoc> path = null;
|
||||
try
|
||||
{
|
||||
final CellNode result = buffer.findPath(gx, gy, gz, gtx, gty, gtz);
|
||||
|
||||
if (debug)
|
||||
{
|
||||
for (CellNode n : buffer.debugPath())
|
||||
{
|
||||
if (n.getCost() < 0)
|
||||
{
|
||||
dropDebugItem(1831, (int) (-n.getCost() * 10), n.getLoc());
|
||||
}
|
||||
else
|
||||
{
|
||||
// known nodes
|
||||
dropDebugItem(Inventory.ADENA_ID, (int) (n.getCost() * 10), n.getLoc());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result == null)
|
||||
{
|
||||
_findFails++;
|
||||
return null;
|
||||
}
|
||||
|
||||
path = constructPath(result);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.log(Level.WARNING, "", e);
|
||||
return null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
buffer.free();
|
||||
}
|
||||
|
||||
if ((path.size() < 3) || (Config.MAX_POSTFILTER_PASSES <= 0))
|
||||
{
|
||||
_findSuccess++;
|
||||
return path;
|
||||
}
|
||||
|
||||
final long timeStamp = System.currentTimeMillis();
|
||||
_postFilterUses++;
|
||||
if (playable)
|
||||
{
|
||||
_postFilterPlayableUses++;
|
||||
}
|
||||
|
||||
int currentX, currentY, currentZ;
|
||||
ListIterator<AbstractNodeLoc> middlePoint;
|
||||
boolean remove;
|
||||
int pass = 0;
|
||||
do
|
||||
{
|
||||
pass++;
|
||||
_postFilterPasses++;
|
||||
|
||||
remove = false;
|
||||
middlePoint = path.listIterator();
|
||||
currentX = x;
|
||||
currentY = y;
|
||||
currentZ = z;
|
||||
|
||||
while (middlePoint.hasNext())
|
||||
{
|
||||
final AbstractNodeLoc locMiddle = middlePoint.next();
|
||||
if (!middlePoint.hasNext())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
final AbstractNodeLoc locEnd = path.get(middlePoint.nextIndex());
|
||||
if (GeoData.getInstance().canMove(currentX, currentY, currentZ, locEnd.getX(), locEnd.getY(), locEnd.getZ(), instance))
|
||||
{
|
||||
middlePoint.remove();
|
||||
remove = true;
|
||||
if (debug)
|
||||
{
|
||||
dropDebugItem(735, 1, locMiddle);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
currentX = locMiddle.getX();
|
||||
currentY = locMiddle.getY();
|
||||
currentZ = locMiddle.getZ();
|
||||
}
|
||||
}
|
||||
}
|
||||
// only one postfilter pass for AI
|
||||
while (playable && remove && (path.size() > 2) && (pass < Config.MAX_POSTFILTER_PASSES));
|
||||
|
||||
if (debug)
|
||||
{
|
||||
path.forEach(n -> dropDebugItem(65, 1, n));
|
||||
}
|
||||
|
||||
_findSuccess++;
|
||||
_postFilterElapsed += System.currentTimeMillis() - timeStamp;
|
||||
return path;
|
||||
}
|
||||
|
||||
private List<AbstractNodeLoc> constructPath(AbstractNode<NodeLoc> node)
|
||||
{
|
||||
final LinkedList<AbstractNodeLoc> path = new LinkedList<>();
|
||||
int previousDirectionX = Integer.MIN_VALUE;
|
||||
int previousDirectionY = Integer.MIN_VALUE;
|
||||
int directionX, directionY;
|
||||
|
||||
while (node.getParent() != null)
|
||||
{
|
||||
if (!Config.ADVANCED_DIAGONAL_STRATEGY && (node.getParent().getParent() != null))
|
||||
{
|
||||
final int tmpX = node.getLoc().getNodeX() - node.getParent().getParent().getLoc().getNodeX();
|
||||
final int tmpY = node.getLoc().getNodeY() - node.getParent().getParent().getLoc().getNodeY();
|
||||
if (Math.abs(tmpX) == Math.abs(tmpY))
|
||||
{
|
||||
directionX = tmpX;
|
||||
directionY = tmpY;
|
||||
}
|
||||
else
|
||||
{
|
||||
directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX();
|
||||
directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
directionX = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX();
|
||||
directionY = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY();
|
||||
}
|
||||
|
||||
// only add a new route point if moving direction changes
|
||||
if ((directionX != previousDirectionX) || (directionY != previousDirectionY))
|
||||
{
|
||||
previousDirectionX = directionX;
|
||||
previousDirectionY = directionY;
|
||||
|
||||
path.addFirst(node.getLoc());
|
||||
node.setLoc(null);
|
||||
}
|
||||
|
||||
node = node.getParent();
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
private CellNodeBuffer alloc(int size, boolean playable)
|
||||
{
|
||||
CellNodeBuffer current = null;
|
||||
for (BufferInfo i : _allBuffers)
|
||||
{
|
||||
if (i.mapSize >= size)
|
||||
{
|
||||
for (CellNodeBuffer buf : i.bufs)
|
||||
{
|
||||
if (buf.lock())
|
||||
{
|
||||
i.uses++;
|
||||
if (playable)
|
||||
{
|
||||
i.playableUses++;
|
||||
}
|
||||
i.elapsed += buf.getElapsedTime();
|
||||
current = buf;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (current != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// not found, allocate temporary buffer
|
||||
current = new CellNodeBuffer(i.mapSize);
|
||||
current.lock();
|
||||
if (i.bufs.size() < i.count)
|
||||
{
|
||||
i.bufs.add(current);
|
||||
i.uses++;
|
||||
if (playable)
|
||||
{
|
||||
i.playableUses++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
i.overflows++;
|
||||
if (playable)
|
||||
{
|
||||
i.playableOverflows++;
|
||||
// System.err.println("Overflow, size requested: " + size + " playable:"+playable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
private void dropDebugItem(int itemId, int num, AbstractNodeLoc loc)
|
||||
{
|
||||
final L2ItemInstance item = new L2ItemInstance(IdFactory.getInstance().getNextId(), itemId);
|
||||
item.setCount(num);
|
||||
item.spawnMe(loc.getX(), loc.getY(), loc.getZ());
|
||||
_debugItems.add(item);
|
||||
}
|
||||
|
||||
private static final class BufferInfo
|
||||
{
|
||||
final int mapSize;
|
||||
final int count;
|
||||
ArrayList<CellNodeBuffer> bufs;
|
||||
int uses = 0;
|
||||
int playableUses = 0;
|
||||
int overflows = 0;
|
||||
int playableOverflows = 0;
|
||||
long elapsed = 0;
|
||||
|
||||
public BufferInfo(int size, int cnt)
|
||||
{
|
||||
mapSize = size;
|
||||
count = cnt;
|
||||
bufs = new ArrayList<>(count);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
final StringBuilder sb = new StringBuilder(100);
|
||||
sb.append(mapSize);
|
||||
sb.append("x");
|
||||
sb.append(mapSize);
|
||||
sb.append(" num:");
|
||||
sb.append(bufs.size());
|
||||
sb.append("/");
|
||||
sb.append(count);
|
||||
sb.append(" uses:");
|
||||
sb.append(uses);
|
||||
sb.append("/");
|
||||
sb.append(playableUses);
|
||||
if (uses > 0)
|
||||
{
|
||||
sb.append(" total/avg(ms):");
|
||||
sb.append(elapsed);
|
||||
sb.append("/");
|
||||
sb.append(String.format("%1.2f", (double) elapsed / uses));
|
||||
}
|
||||
|
||||
sb.append(" ovf:");
|
||||
sb.append(overflows);
|
||||
sb.append("/");
|
||||
sb.append(playableOverflows);
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getStat()
|
||||
{
|
||||
final String[] result = new String[_allBuffers.length + 1];
|
||||
for (int i = 0; i < _allBuffers.length; i++)
|
||||
{
|
||||
result[i] = _allBuffers[i].toString();
|
||||
}
|
||||
|
||||
final StringBuilder sb = new StringBuilder(128);
|
||||
sb.append("LOS postfilter uses:");
|
||||
sb.append(_postFilterUses);
|
||||
sb.append("/");
|
||||
sb.append(_postFilterPlayableUses);
|
||||
if (_postFilterUses > 0)
|
||||
{
|
||||
sb.append(" total/avg(ms):");
|
||||
sb.append(_postFilterElapsed);
|
||||
sb.append("/");
|
||||
sb.append(String.format("%1.2f", (double) _postFilterElapsed / _postFilterUses));
|
||||
sb.append(" passes total/avg:");
|
||||
sb.append(_postFilterPasses);
|
||||
sb.append("/");
|
||||
sb.append(String.format("%1.1f", (double) _postFilterPasses / _postFilterUses));
|
||||
sb.append(Config.EOL);
|
||||
}
|
||||
sb.append("Pathfind success/fail:");
|
||||
sb.append(_findSuccess);
|
||||
sb.append("/");
|
||||
sb.append(_findFails);
|
||||
result[result.length - 1] = sb.toString();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static class SingletonHolder
|
||||
{
|
||||
protected static final CellPathFinding _instance = new CellPathFinding();
|
||||
}
|
||||
}
|
||||
|
@ -1,167 +1,184 @@
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General 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.pathfinding.cellnodes;
|
||||
|
||||
import com.l2jmobius.commons.geodriver.Cell;
|
||||
import com.l2jmobius.gameserver.GeoData;
|
||||
import com.l2jmobius.gameserver.pathfinding.AbstractNodeLoc;
|
||||
|
||||
/**
|
||||
* @author -Nemesiss-, HorridoJoho
|
||||
*/
|
||||
public class NodeLoc extends AbstractNodeLoc
|
||||
{
|
||||
private int _x;
|
||||
private int _y;
|
||||
private boolean _goNorth;
|
||||
private boolean _goEast;
|
||||
private boolean _goSouth;
|
||||
private boolean _goWest;
|
||||
private int _geoHeight;
|
||||
|
||||
public NodeLoc(int x, int y, int z)
|
||||
{
|
||||
set(x, y, z);
|
||||
}
|
||||
|
||||
public void set(int x, int y, int z)
|
||||
{
|
||||
_x = x;
|
||||
_y = y;
|
||||
_goNorth = GeoData.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH);
|
||||
_goEast = GeoData.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST);
|
||||
_goSouth = GeoData.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH);
|
||||
_goWest = GeoData.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST);
|
||||
_geoHeight = GeoData.getInstance().getNearestZ(x, y, z);
|
||||
}
|
||||
|
||||
public boolean canGoNorth()
|
||||
{
|
||||
return _goNorth;
|
||||
}
|
||||
|
||||
public boolean canGoEast()
|
||||
{
|
||||
return _goEast;
|
||||
}
|
||||
|
||||
public boolean canGoSouth()
|
||||
{
|
||||
return _goSouth;
|
||||
}
|
||||
|
||||
public boolean canGoWest()
|
||||
{
|
||||
return _goWest;
|
||||
}
|
||||
|
||||
public boolean canGoNone()
|
||||
{
|
||||
return !canGoNorth() && !canGoEast() && !canGoSouth() && !canGoWest();
|
||||
}
|
||||
|
||||
public boolean canGoAll()
|
||||
{
|
||||
return canGoNorth() && canGoEast() && canGoSouth() && canGoWest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getX()
|
||||
{
|
||||
return GeoData.getInstance().getWorldX(_x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getY()
|
||||
{
|
||||
return GeoData.getInstance().getWorldY(_y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getZ()
|
||||
{
|
||||
return _geoHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setZ(short z)
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNodeX()
|
||||
{
|
||||
return _x;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNodeY()
|
||||
{
|
||||
return _y;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = (prime * result) + _x;
|
||||
result = (prime * result) + _y;
|
||||
|
||||
int nswe = 0;
|
||||
if (canGoNorth())
|
||||
{
|
||||
nswe |= Cell.NSWE_NORTH;
|
||||
}
|
||||
if (canGoEast())
|
||||
{
|
||||
nswe |= Cell.NSWE_EAST;
|
||||
}
|
||||
if (canGoSouth())
|
||||
{
|
||||
nswe |= Cell.NSWE_SOUTH;
|
||||
}
|
||||
if (canGoWest())
|
||||
{
|
||||
nswe |= Cell.NSWE_WEST;
|
||||
}
|
||||
|
||||
result = (prime * result) + (((_geoHeight & 0xFFFF) << 1) | nswe);
|
||||
return result;
|
||||
// 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;
|
||||
return (_x == other._x) && (_y == other._y) && (!_goNorth == !other._goNorth) && (!_goEast == !other._goEast) && (!_goSouth == !other._goSouth) && (!_goWest == !other._goWest) && (_geoHeight == other._geoHeight);
|
||||
}
|
||||
}
|
||||
/*
|
||||
* This file is part of the L2J Mobius project.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General 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.pathfinding.cellnodes;
|
||||
|
||||
import com.l2jmobius.commons.geodriver.Cell;
|
||||
import com.l2jmobius.gameserver.GeoData;
|
||||
import com.l2jmobius.gameserver.pathfinding.AbstractNodeLoc;
|
||||
|
||||
/**
|
||||
* @author -Nemesiss-, HorridoJoho
|
||||
*/
|
||||
public class NodeLoc extends AbstractNodeLoc
|
||||
{
|
||||
private int _x;
|
||||
private int _y;
|
||||
private boolean _goNorth;
|
||||
private boolean _goEast;
|
||||
private boolean _goSouth;
|
||||
private boolean _goWest;
|
||||
private int _geoHeight;
|
||||
|
||||
public NodeLoc(int x, int y, int z)
|
||||
{
|
||||
set(x, y, z);
|
||||
}
|
||||
|
||||
public void set(int x, int y, int z)
|
||||
{
|
||||
_x = x;
|
||||
_y = y;
|
||||
_goNorth = GeoData.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_NORTH);
|
||||
_goEast = GeoData.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_EAST);
|
||||
_goSouth = GeoData.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_SOUTH);
|
||||
_goWest = GeoData.getInstance().checkNearestNswe(x, y, z, Cell.NSWE_WEST);
|
||||
_geoHeight = GeoData.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 GeoData.getInstance().getWorldX(_x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getY()
|
||||
{
|
||||
return GeoData.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;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user