Package com.l2jfrozen.gameserver.geo.pathfinding

Source Code of com.l2jfrozen.gameserver.geo.pathfinding.PathFinding

/*
* 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 2, 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, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
* http://www.gnu.org/copyleft/gpl.html
*/
package com.l2jfrozen.gameserver.geo.pathfinding;

import java.util.ArrayList;

import org.apache.commons.lang.ArrayUtils;

import com.l2jfrozen.Config;
import com.l2jfrozen.gameserver.geo.GeoData;
import com.l2jfrozen.gameserver.geo.pathfinding.cellnodes.CellPathFinding;
import com.l2jfrozen.gameserver.geo.pathfinding.geonodes.GeoPathFinding;
import com.l2jfrozen.gameserver.geo.pathfinding.utils.BinaryNodeHeap;
import com.l2jfrozen.gameserver.geo.pathfinding.utils.CellNodeMap;
import com.l2jfrozen.gameserver.geo.util.L2Arrays;
import com.l2jfrozen.gameserver.geo.util.L2Collections;
import com.l2jfrozen.gameserver.geo.util.L2FastSet;
import com.l2jfrozen.gameserver.model.L2World;

/**
* @author -Nemesiss-
*/
public abstract class PathFinding
{
  public static PathFinding getInstance()
  {
    if(!Config.GEODATA_CELLFINDING)
      return GeoPathFinding.getInstance(); //Higher Memory Usage, Smaller Cpu Usage
    return CellPathFinding.getInstance(); // Cell pathfinding, calculated directly from geodata files
  }

  public abstract Node[] findPath(int x, int y, int z, int tx, int ty, int tz);

  public abstract Node[] readNeighbors(Node n, int idx);

  public final Node[] 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
    L2FastSet<Node> visited = L2Collections.newL2FastSet();

    // List of Nodes to Visit
    L2FastSet<Node> to_visit = L2Collections.newL2FastSet();
    to_visit.add(start);
    try
    {
      int i = 0;
      while(i < 800)
      {
        if(to_visit.isEmpty())
        {
          // No Path found
          return null;
        }

        Node node = to_visit.removeFirst();

        if(node.equals(end)) //path found!
          return constructPath(node);
        i++;
        visited.add(node);
        node.attachNeighbors();
        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;
    }
    finally
    {
      L2Collections.recycle(visited);
      L2Collections.recycle(to_visit);
    }
  }
 
  public final Node[] searchByClosest(Node start, Node end)
  {
    // Note: This is the version for cell-based calculation, harder
    // on cpu than from block-based pathnode files. However produces better routes.
   
    // 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 8000 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
    CellNodeMap known = CellNodeMap.newInstance();
    // List of Nodes to Visit
    ArrayList<Node> to_visit = L2Collections.newArrayList();
    to_visit.add(start);
    known.add(start);
    try
    {
      int targetx = end.getNodeX();
      int targety = end.getNodeY();
      int targetz = end.getZ();
      int dx, dy, dz;
      boolean added;
      int i = 0;
      while(i < 3500)
      {
        if(to_visit.isEmpty())
        {
          // No Path found
          return null;
        }

        Node node = to_visit.remove(0);

        i++;

        node.attachNeighbors();
        if(node.equals(end))
        {
          //path found! note that node z coordinate is updated only in attach
          //to improve performance (alternative: much more checks)
          //System.out.println("path found, i:"+i);
          return constructPath(node);
        }

        Node[] neighbors = node.getNeighbors();
        if(neighbors == null)
          continue;
        for(Node n : neighbors)
        {
          if(!known.contains(n))
          {
            added = false;
            n.setParent(node);
            dx = targetx - n.getNodeX();
            dy = targety - n.getNodeY();
            dz = targetz - n.getZ();
            n.setCost(dx * dx + dy * dy + dz / 2 * dz/*+n.getCost()*/);
            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.add(n);
            known.add(n);
          }
        }
      }
      //No Path found
      //System.out.println("no path found");
      return null;
    }
    finally
    {
      CellNodeMap.recycle(known);
      L2Collections.recycle(to_visit);
    }
  }
 
  public final Node[] searchByClosest2(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
    L2FastSet<Node> visited = L2Collections.newL2FastSet();
    // List of Nodes to Visit
    ArrayList<Node> to_visit = L2Collections.newArrayList();
    to_visit.add(start);
    try
    {
      int targetx = end.getNodeX();
      int targety = end.getNodeY();
      int dx, dy;
      boolean added;
      int i = 0;
      while(i < 550)
      {
        if(to_visit.isEmpty())
        {
          // No Path found
          return null;
        }

        Node node = to_visit.remove(0);

        if(node.equals(end)) //path found!
        {
          return constructPath2(node);
        }
        i++;
        visited.add(node);
        node.attachNeighbors();
        Node[] neighbors = node.getNeighbors();
        if(neighbors == null)
          continue;
        for(Node n : neighbors)
        {
          if(!visited.contains(n) && !to_visit.contains(n))
          {
            added = false;
            n.setParent(node);
            dx = targetx - n.getNodeX();
            dy = targety - n.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.add(n);
          }
        }
      }
      //No Path found
      return null;
    }
    finally
    {
      L2Collections.recycle(visited);
      L2Collections.recycle(to_visit);
    }
  }
 
  public final Node[] searchAStar(Node start, Node end)
  {
    // Not operational yet?
    int start_x = start.getX();
    int start_y = start.getY();
    int end_x = end.getX();
    int end_y = end.getY();
    //List of Visited Nodes
    L2FastSet<Node> visited = L2Collections.newL2FastSet();//TODO! Add limit to cfg

    // List of Nodes to Visit
    BinaryNodeHeap to_visit = BinaryNodeHeap.newInstance();
    to_visit.add(start);
    try
    {
      int i = 0;
      while(i < 800)//TODO! Add limit to cfg
      {
        if(to_visit.isEmpty())
        {
          // No Path found
          return null;
        }

        Node node;
        try
        {
          node = to_visit.removeFirst();
        }
        catch(Exception e)
        {
          // No Path found
          if(Config.ENABLE_ALL_EXCEPTIONS)
            e.printStackTrace();
         
          return null;
        }
        if(node.equals(end)) //path found!
          return constructPath(node);
        visited.add(node);
        node.attachNeighbors();
        for(Node n : node.getNeighbors())
        {
          if(!visited.contains(n) && !to_visit.contains(n))
          {
            i++;
            n.setParent(node);
            n.setCost(Math.abs(start_x - n.getNodeX())
              + Math.abs(start_y - n.getNodeY()) + Math.abs(end_x - n.getNodeX())
              + Math.abs(end_y - n.getNodeY()));
            to_visit.add(n);
          }
        }
      }
      //No Path found
      return null;
    }
    finally
    {
      L2Collections.recycle(visited);
      BinaryNodeHeap.recycle(to_visit);
    }
  }
 
  public final Node[] constructPath(Node node)
  {
    ArrayList<Node> tmp = L2Collections.newArrayList();

    while(node.getParent() != null)
    {
      tmp.add(node);

      node = node.getParent();
    }

    Node[] path = tmp.toArray(new Node[tmp.size()]);

    L2Collections.recycle(tmp);

    ArrayUtils.reverse(path);

    for(int lastValid = 0; lastValid < path.length - 1;)
    {
      final Node lastValidNode = path[lastValid];

      int low = lastValid;
      int high = path.length - 1;

      while(low < high)
      {
        final int mid = ((low + high) >> 1) + 1;
        final Node midNode = path[mid];
        final int delta = mid - lastValid;
        final int deltaNodeX = Math.abs(midNode.getNodeX() - lastValidNode.getNodeX());
        final int deltaNodeY = Math.abs(midNode.getNodeY() - lastValidNode.getNodeY());

        if(delta <= 1)
        {
          low = mid;
        }
        else if(delta % 2 == 0 && deltaNodeX == delta / 2 && deltaNodeY == delta / 2)
        {
          low = mid;
        }
        else if(deltaNodeX == delta || deltaNodeY == delta)
        {
          low = mid;
        }
        else if(GeoData.getInstance().canMoveFromToTarget(lastValidNode.getX(), lastValidNode.getY(), lastValidNode.getZ(), midNode.getX(), midNode.getY(), midNode.getZ()))
        {
          low = mid;
        }
        else
        {
          high = mid - 1;
        }
      }

      final int nextValid = low;
     
      for(int i = lastValid + 1; i < nextValid; i++)
        path[i] = null;
     
      lastValid = nextValid;
    }

    return L2Arrays.compact(path);
  }

  public final Node[] constructPath2(Node node)
  {
    ArrayList<Node> tmp = L2Collections.newArrayList();
    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.getNodeX() - node.getParent().getNodeX();
      directiony = node.getNodeY() - node.getParent().getNodeY();
      if(directionx != previousdirectionx || directiony != previousdirectiony)
      {
        previousdirectionx = directionx;
        previousdirectiony = directiony;
        tmp.add(node);
      }
      node = node.getParent();
    }

    Node[] path = tmp.toArray(new Node[tmp.size()]);

    L2Collections.recycle(tmp);

    ArrayUtils.reverse(path);

    return path;
  }

  /**
   * Convert geodata position to pathnode position
   *
   * @param geo_pos
   * @return pathnode position
   */
  public final short getNodePos(int geo_pos)
  {
    return (short)(geo_pos >> 3); //OK?
  }

  /**
   * Convert node position to pathnode block position
   *
   * @param node_pos
   * @return pathnode block position (0...255)
   */
  public final short getNodeBlock(int node_pos)
  {
    return (short)(node_pos % 256);
  }

  public final byte getRegionX(int node_pos)
  {
    return (byte)((node_pos >> 8) + 16);
  }

  public final byte getRegionY(int node_pos)
  {
    return (byte)((node_pos >> 8) + 10);
  }

  public final short getRegionOffset(byte rx, byte ry)
  {
    return (short)((rx << 5) + ry);
  }

  /**
   * Convert pathnode x to World x position
   *
   * @param node_x
   * @return
   */
  public final 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 final int calculateWorldY(short node_y)
  {
    return L2World.MAP_MIN_Y + node_y * 128 + 48;
  }
}
TOP

Related Classes of com.l2jfrozen.gameserver.geo.pathfinding.PathFinding

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.