/* * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ package net.sf.l2j.gameserver.pathfinding; import java.util.LinkedList; import java.util.List; import net.sf.l2j.gameserver.model.L2World; import net.sf.l2j.gameserver.pathfinding.geonodes.GeoPathFinding; import net.sf.l2j.gameserver.pathfinding.utils.BinaryNodeHeap; import net.sf.l2j.gameserver.pathfinding.utils.FastNodeList; /** * * @author -Nemesiss- */ public abstract class PathFinding { private static PathFinding _instance; public static PathFinding getInstance() { if (_instance == null) { if (true /*Config.GEODATA_PATHFINDING*/) { //Smaler Memory Usage, Higher Cpu Usage (CalculatedOnTheFly) return GeoPathFinding.getInstance(); } else // WORLD_PATHFINDING { //Higher Memoru Usage, Lower Cpu Usage (PreCalculated) } } return _instance; } public abstract boolean pathNodesExist(short regionoffset); public abstract List findPath(int x, int y, int z, int tx, int ty, int tz); public abstract Node[] readNeighbors(short node_x,short node_y, int idx); public List search(Node start, Node end) { // The simplest grid-based pathfinding. // Drawback is not having higher cost for diagonal movement (means funny routes) // Could be optimized e.g. not to calculate backwards as far as forwards. // List of Visited Nodes LinkedList visited = new LinkedList(); // List of Nodes to Visit LinkedList to_visit = new LinkedList(); to_visit.add(start); int i = 0; while (i < 800)//TODO! Add limit to cfg. Worst case max distance is 1810.. { Node node; try { node = to_visit.removeFirst(); } catch (Exception e) { // No Path found return null; } if (node.equals(end)) //path found! return constructPath(node); else { i++; visited.add(node); node.attacheNeighbors(); Node[] neighbors = node.getNeighbors(); if (neighbors == null) continue; for (Node n : neighbors) { if (!visited.contains(n) && !to_visit.contains(n)) { n.setParent(node); to_visit.add(n); } } } } //No Path found return null; } public List searchByClosest(Node start, Node end) { // Always continues checking from the closest to target non-blocked // node from to_visit list. There's extra length in path if needed // to go backwards/sideways but when moving generally forwards, this is extra fast // and accurate. And can reach insane distances (try it with 800 nodes..). // Minimum required node count would be around 300-400. // Generally returns a bit (only a bit) more intelligent looking routes than // the basic version. Not a true distance image (which would increase CPU // load) level of intelligence though. // List of Visited Nodes FastNodeList visited = new FastNodeList(550); // List of Nodes to Visit LinkedList to_visit = new LinkedList(); to_visit.add(start); short targetx = end.getLoc().getNodeX(); short targety = end.getLoc().getNodeY(); int dx, dy; boolean added; int i = 0; while (i < 550) { Node node; try { node = to_visit.removeFirst(); } catch (Exception e) { // No Path found return null; } if (node.equals(end)) //path found! return constructPath(node); else { i++; visited.add(node); node.attacheNeighbors(); Node[] neighbors = node.getNeighbors(); if (neighbors == null) continue; for (Node n : neighbors) { if (!visited.containsRev(n) && !to_visit.contains(n)) { added = false; n.setParent(node); dx = targetx - n.getLoc().getNodeX(); dy = targety - n.getLoc().getNodeY(); n.setCost(dx*dx+dy*dy); for (int index = 0; index < to_visit.size(); index++) { // supposed to find it quite early.. if (to_visit.get(index).getCost() > n.getCost()) { to_visit.add(index, n); added = true; break; } } if (!added) to_visit.addLast(n); } } } } //No Path found return null; } public List searchAStar(Node start, Node end) { // Not operational yet? int start_x = start.getLoc().getX(); int start_y = start.getLoc().getY(); int end_x = end.getLoc().getX(); int end_y = end.getLoc().getY(); //List of Visited Nodes FastNodeList visited = new FastNodeList(800);//TODO! Add limit to cfg // List of Nodes to Visit BinaryNodeHeap to_visit = new BinaryNodeHeap(800); to_visit.add(start); int i = 0; while (i < 800)//TODO! Add limit to cfg { Node node; try { node = to_visit.removeFirst(); } catch (Exception e) { // No Path found return null; } if (node.equals(end)) //path found! return constructPath(node); else { visited.add(node); node.attacheNeighbors(); for (Node n : node.getNeighbors()) { if (!visited.contains(n) && !to_visit.contains(n)) { i++; n.setParent(node); n.setCost(Math.abs(start_x - n.getLoc().getNodeX())+Math.abs(start_y - n.getLoc().getNodeY()) +Math.abs(end_x - n.getLoc().getNodeX())+Math.abs(end_y - n.getLoc().getNodeY())); to_visit.add(n); } } } } //No Path found return null; } public List constructPath(Node node) { LinkedList path = new LinkedList(); int previousdirectionx = -1000; int previousdirectiony = -1000; int directionx; int directiony; while (node.getParent() != null) { // only add a new route point if moving direction changes directionx = node.getLoc().getNodeX() - node.getParent().getLoc().getNodeX(); directiony = node.getLoc().getNodeY() - node.getParent().getLoc().getNodeY(); if(directionx != previousdirectionx || directiony != previousdirectiony) { previousdirectionx = directionx; previousdirectiony = directiony; path.addFirst(node.getLoc()); } node = node.getParent(); } return path; } /** * Convert geodata position to pathnode position * @param geo_pos * @return pathnode position */ public short getNodePos(int geo_pos) { return (short)(geo_pos >> 3); //OK? } /** * Convert node position to pathnode block position * @param geo_pos * @return pathnode block position (0...255) */ public short getNodeBlock(int node_pos) { return (short)(node_pos % 256); } public byte getRegionX(int node_pos) { return (byte)((node_pos >> 8) + 16); } public byte getRegionY(int node_pos) { return (byte)((node_pos >> 8) + 10); } public short getRegionOffset(byte rx, byte ry) { return (short)((rx << 5) + ry); } /** * Convert pathnode x to World x position * @param node_x, rx * @return */ public int calculateWorldX(short node_x) { return L2World.MAP_MIN_X + node_x * 128 + 48 ; } /** * Convert pathnode y to World y position * @param node_y * @return */ public int calculateWorldY(short node_y) { return L2World.MAP_MIN_Y + node_y * 128 + 48 ; } }