Package com.l2jfrozen.gameserver.geo

Source Code of com.l2jfrozen.gameserver.geo.GeoEngine$SingletonHolder

/*
* 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;

import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.StringTokenizer;
import java.util.logging.Level;

import com.l2jfrozen.Config;
import com.l2jfrozen.gameserver.datatables.csv.DoorTable;
import com.l2jfrozen.gameserver.geo.pathfinding.Node;
import com.l2jfrozen.gameserver.geo.pathfinding.cellnodes.CellPathFinding;
import com.l2jfrozen.gameserver.geo.util.L2Arrays;
import com.l2jfrozen.gameserver.geo.util.LookupTable;
import com.l2jfrozen.gameserver.model.L2Object;
import com.l2jfrozen.gameserver.model.L2World;
import com.l2jfrozen.gameserver.model.Location;
import com.l2jfrozen.gameserver.model.actor.instance.L2DoorInstance;
import com.l2jfrozen.gameserver.model.actor.instance.L2PcInstance;
import com.l2jfrozen.gameserver.model.actor.instance.L2SiegeGuardInstance;
import com.l2jfrozen.util.Point3D;

public final class GeoEngine extends GeoData
{
  private final static byte _e = 1;
  private final static byte _w = 2;
  private final static byte _s = 4;
  private final static byte _n = 8;
 
  private static final class SingletonHolder
  {
    protected static final GeoEngine INSTANCE = new GeoEngine();
  }
 
  public static GeoEngine getInstance()
  {
    return SingletonHolder.INSTANCE;
  }
 
  private final LookupTable<MappedByteBuffer> _geodata = new LookupTable<MappedByteBuffer>();
  private final LookupTable<IntBuffer> _geodataIndex = new LookupTable<IntBuffer>();
  private BufferedOutputStream _geoBugsOut;
 
  protected GeoEngine()
  {
    nInitGeodata();
  }

  @Override
  public short getType(int x, int y)
  {
    return nGetType((x - L2World.MAP_MIN_X) >> 4, (y - L2World.MAP_MIN_Y) >> 4);
  }

  @Override
  public short getHeight(int x, int y, int z)
  {
    return nGetHeight((x - L2World.MAP_MIN_X) >> 4,(y - L2World.MAP_MIN_Y) >> 4, z);
  }

  @Override
  public short getSpawnHeight(int x, int y, int zmin, int zmax, int spawnid)
  {
    return nGetSpawnHeight((x - L2World.MAP_MIN_X) >> 4, (y - L2World.MAP_MIN_Y) >> 4, zmin, zmax, spawnid);
  }

  @Override
  public String geoPosition(int x, int y)
  {
    int gx = (x - L2World.MAP_MIN_X) >> 4;
    int gy = (y - L2World.MAP_MIN_Y) >> 4;
    return "bx: " + getBlock(gx) + " by: " + getBlock(gy) + " cx: " + getCell(gx) + " cy: " + getCell(gy) + "  region offset: " + getRegionOffset(gx, gy);
  }

  @Override
  public boolean canSeeTarget(L2Object cha, Point3D target)
  {
    if(DoorTable.getInstance().checkIfDoorsBetween(cha.getX(), cha.getY(), cha.getZ(), target.getX(), target.getY(), target.getZ()))
      return false;
    if(cha.getZ() >= target.getZ())
      return canSeeTarget(cha.getX(), cha.getY(), cha.getZ(), target.getX(), target.getY(), target.getZ());
    return canSeeTarget(target.getX(), target.getY(), target.getZ(), cha.getX(), cha.getY(), cha.getZ());
  }

  @Override
  public boolean canSeeTarget(L2Object cha, L2Object target)
  {
    // To be able to see over fences and give the player the viewpoint
    // game client has, all coordinates are lifted 45 from ground.
    // Because of layer selection in LOS algorithm (it selects -45 there
    // and some layers can be very close...) do not change this without
    // changing the LOS code.
    // Basically the +45 is character height. Raid bosses are naturally higher,
    // dwarves shorter, but this should work relatively well.
    // If this is going to be improved, use e.g.
    // ((L2Character)cha).getTemplate().collisionHeight
    int z = cha.getZ() + 45;
    if(cha instanceof L2SiegeGuardInstance)
      z += 30; // well they don't move closer to balcony fence at the moment :(
    int z2 = target.getZ() + 45;
    if(target instanceof L2DoorInstance)
      return true; // door coordinates are hinge coords..
    if(DoorTable.getInstance().checkIfDoorsBetween(cha.getX(), cha.getY(), z, target.getX(), target.getY(), z2))
      return false;
    if(target instanceof L2SiegeGuardInstance)
      z2 += 30; // well they don't move closer to balcony fence at the moment :(
    if(z >= z2)
      return canSeeTarget(cha.getX(), cha.getY(), z, target.getX(), target.getY(), z2);
    return canSeeTarget(target.getX(), target.getY(), z2, cha.getX(), cha.getY(), z);
  }

  @Override
  public boolean canSeeTargetDebug(L2PcInstance gm, L2Object target)
  {
    // comments: see above
    int z = gm.getZ() + 45;
    int z2 = target.getZ() + 45;
    if(target instanceof L2DoorInstance)
    {
      gm.sendMessage("door always true");
      return true; // door coordinates are hinge coords..
    }

    if(z >= z2)
      return canSeeDebug(gm, (gm.getX() - L2World.MAP_MIN_X) >> 4, (gm.getY() - L2World.MAP_MIN_Y) >> 4, z, (target.getX() - L2World.MAP_MIN_X) >> 4, (target.getY() - L2World.MAP_MIN_Y) >> 4, z2);
    return canSeeDebug(gm, (target.getX() - L2World.MAP_MIN_X) >> 4, (target.getY() - L2World.MAP_MIN_Y) >> 4, z2, (gm.getX() - L2World.MAP_MIN_X) >> 4, (gm.getY() - L2World.MAP_MIN_Y) >> 4, z);
  }

  @Override
  public short getNSWE(int x, int y, int z)
  {
    return nGetNSWE((x - L2World.MAP_MIN_X) >> 4, (y - L2World.MAP_MIN_Y) >> 4, z);
  }
 
  @Override
  public boolean canMoveFromToTarget(int x, int y, int z, int tx, int ty, int tz)
  {
    Location destiny = moveCheck(x, y, z, tx, ty, tz);
    return (destiny.getX() == tx && destiny.getY() == ty && destiny.getZ() == tz);
  }
 
  @Override
  public Location moveCheck(int x, int y, int z, int tx, int ty, int tz)
  {
    Location startpoint = new Location(x, y, z);
    if(DoorTable.getInstance().checkIfDoorsBetween(x, y, z, tx, ty, tz))
      return startpoint;

    Location destiny = new Location(tx, ty, tz);
    return moveCheck(startpoint, destiny, (x - L2World.MAP_MIN_X) >> 4, (y - L2World.MAP_MIN_Y) >> 4, z, (tx - L2World.MAP_MIN_X) >> 4, (ty - L2World.MAP_MIN_Y) >> 4, tz);
  }

  @Override
  public void addGeoDataBug(L2PcInstance gm, String comment)
  {
    int gx = (gm.getX() - L2World.MAP_MIN_X) >> 4;
    int gy = (gm.getY() - L2World.MAP_MIN_Y) >> 4;
    int bx = getBlock(gx);
    int by = getBlock(gy);
    int cx = getCell(gx);
    int cy = getCell(gy);
    int rx = (gx >> 11) + 10;
    int ry = (gy >> 11) + 10;
    String out = rx + ";" + ry + ";" + bx + ";" + by + ";" + cx + ";" + cy + ";" + gm.getZ() + ";" + comment + "\n";
    try
    {
      _geoBugsOut.write(out.getBytes());
      _geoBugsOut.flush();
      gm.sendMessage("GeoData bug saved!");
    }
    catch(Exception e)
    {
      e.printStackTrace();
      gm.sendMessage("GeoData bug save Failed!");
    }
  }

  @Override
  public boolean canSeeTarget(int x, int y, int z, int tx, int ty, int tz)
  {
    return canSee((x - L2World.MAP_MIN_X) >> 4, (y - L2World.MAP_MIN_Y) >> 4, z, (tx - L2World.MAP_MIN_X) >> 4, (ty - L2World.MAP_MIN_Y) >> 4, tz);
  }

  @Override
  public boolean hasGeo(int x, int y)
  {
    int gx = (x - L2World.MAP_MIN_X) >> 4;
    int gy = (y - L2World.MAP_MIN_Y) >> 4;
    short region = getRegionOffset(gx, gy);

    return _geodata.get(region) != null;
  }
 
  private boolean canSee(int x, int y, double z, int tx, int ty, int tz)
  {
    int dx = (tx - x);
    int dy = (ty - y);
    final double dz = (tz - z);
    final int distance2 = dx * dx + dy * dy;

    if(distance2 > 90000) // (300*300) 300*16 = 4800 in world coord
    {
      //Avoid too long check
      return false;
    }
    // very short checks: 9 => 144 world distance
    // this ensures NLOS function has enough points to calculate,
    // it might not work when distance is small and path vertical
    else if(distance2 < 82)
    {
      // 150 should be too deep/high.
      if(dz * dz > 22500)
      {
        short region = getRegionOffset(x, y);
        // geodata is loaded for region and mobs should have correct Z coordinate...
        // so there would likely be a floor in between the two
        if(_geodata.get(region) != null)
          return false;
      }
      return true;
    }

    // Increment in Z coordinate when moving along X or Y axis
    // and not straight to the target. This is done because
    // calculation moves either in X or Y direction.
    final int inc_x = sign(dx);
    final int inc_y = sign(dy);
    dx = Math.abs(dx);
    dy = Math.abs(dy);
    final double inc_z_directionx = dz * dx / (distance2);
    final double inc_z_directiony = dz * dy / (distance2);

    // next_* are used in NLOS check from x,y
    int next_x = x;
    int next_y = y;

    // creates path to the target
    // calculation stops when next_* == target
    if(dx >= dy)// dy/dx <= 1
    {
      int delta_A = 2 * dy;
      int d = delta_A - dx;
      int delta_B = delta_A - 2 * dx;

      for(int i = 0; i < dx; i++)
      {
        x = next_x;
        y = next_y;
        if(d > 0)
        {
          d += delta_B;
          next_x += inc_x;
          z += inc_z_directionx;
          if(!nLOS(x, y, (int) z, inc_x, 0, inc_z_directionx, tz, false))
            return false;
          next_y += inc_y;
          z += inc_z_directiony;
          //_log.warning("1: next_x:"+next_x+" next_y"+next_y);
          if(!nLOS(next_x, y, (int) z, 0, inc_y, inc_z_directiony, tz, false))
            return false;
        }
        else
        {
          d += delta_A;
          next_x += inc_x;
          //_log.warning("2: next_x:"+next_x+" next_y"+next_y);
          z += inc_z_directionx;
          if(!nLOS(x, y, (int) z, inc_x, 0, inc_z_directionx, tz, false))
            return false;
        }
      }
    }
    else
    {
      int delta_A = 2*dx;
      int d = delta_A - dy;
      int delta_B = delta_A - 2 * dy;
      for(int i = 0; i < dy; i++)
      {
        x = next_x;
        y = next_y;
        if(d > 0)
        {
          d += delta_B;
          next_y += inc_y;
          z += inc_z_directiony;
          if(!nLOS(x, y, (int) z, 0, inc_y, inc_z_directiony, tz, false))
            return false;
          next_x += inc_x;
          z += inc_z_directionx;
          //_log.warning("3: next_x:"+next_x+" next_y"+next_y);
          if(!nLOS(x, next_y, (int) z, inc_x, 0, inc_z_directionx, tz, false))
            return false;
        }
        else
        {
          d += delta_A;
          next_y += inc_y;
          //_log.warning("4: next_x:"+next_x+" next_y"+next_y);
          z += inc_z_directiony;
          if(!nLOS(x, y, (int) z, 0, inc_y, inc_z_directiony, tz, false))
            return false;
        }
      }
    }
    return true;
  }
  /*
   * Debug function for checking if there's a line of sight between
   * two coordinates.
   *
   * Creates points for line of sight check (x,y,z towards target) and
   * in each point, layer and movement checks are made with NLOS function.
   *
   * Coordinates here are geodata x,y but z coordinate is world coordinate
   */
  private boolean canSeeDebug(L2PcInstance gm, int x, int y, double z, int tx, int ty, int tz)
  {
    int dx = (tx - x);
    int dy = (ty - y);
    final double dz = (tz - z);
    final int distance2 = dx * dx + dy * dy;

    if(distance2 > 90000) // (300*300) 300*16 = 4800 in world coord
    {
      //Avoid too long check
      gm.sendMessage("dist > 300");
      return false;
    }
    // very short checks: 9 => 144 world distance
    // this ensures NLOS function has enough points to calculate,
    // it might not work when distance is small and path vertical
    else if(distance2 < 82)
    {
      // 150 should be too deep/high.
      if(dz * dz > 22500)
      {
        short region = getRegionOffset(x, y);
        // geodata is loaded for region and mobs should have correct Z coordinate...
        // so there would likely be a floor in between the two
        if(_geodata.get(region) != null)
          return false;
      }
      return true;
    }

    // Increment in Z coordinate when moving along X or Y axis
    // and not straight to the target. This is done because
    // calculation moves either in X or Y direction.
    final int inc_x = sign(dx);
    final int inc_y = sign(dy);
    dx = Math.abs(dx);
    dy = Math.abs(dy);
    final double inc_z_directionx = dz * dx / (distance2);
    final double inc_z_directiony = dz * dy / (distance2);

    gm.sendMessage("Los: from X: " + x + "Y: " + y + "--->> X: " + tx + " Y: " + ty);

    // next_* are used in NLOS check from x,y
    int next_x = x;
    int next_y = y;

    // creates path to the target
    // calculation stops when next_* == target
    if(dx >= dy)// dy/dx <= 1
    {
      int delta_A = 2 * dy;
      int d = delta_A - dx;
      int delta_B = delta_A - 2 * dx;

      for(int i = 0; i < dx; i++)
      {
        x = next_x;
        y = next_y;
        if(d > 0)
        {
          d += delta_B;
          next_x += inc_x;
          z += inc_z_directionx;
          if(!nLOS(x, y, (int) z, inc_x, 0, inc_z_directionx, tz, true))
            return false;
          next_y += inc_y;
          z += inc_z_directiony;
          //_log.log(Level.WARNING, "1: next_x:"+next_x+" next_y"+next_y);
          if(!nLOS(next_x, y, (int) z, 0, inc_y, inc_z_directiony, tz, true))
            return false;
        }
        else
        {
          d += delta_A;
          next_x += inc_x;
          //_log.log(Level.WARNING, "2: next_x:"+next_x+" next_y"+next_y);
          z += inc_z_directionx;
          if(!nLOS(x, y, (int) z, inc_x, 0, inc_z_directionx, tz, true))
            return false;
        }
      }
    }
    else
    {
      int delta_A = 2 * dx;
      int d = delta_A - dy;
      int delta_B = delta_A - 2 * dy;
      for(int i = 0; i < dy; i++)
      {
        x = next_x;
        y = next_y;
        if(d > 0)
        {
          d += delta_B;
          next_y += inc_y;
          z += inc_z_directiony;
          if(!nLOS(x, y, (int) z, 0, inc_y, inc_z_directiony, tz, true))
            return false;
          next_x += inc_x;
          z += inc_z_directionx;
          //_log.log(Level.WARNING, "3: next_x:"+next_x+" next_y"+next_y);
          if(!nLOS(x, next_y, (int) z, inc_x, 0, inc_z_directionx, tz, true))
            return false;
        }
        else
        {
          d += delta_A;
          next_y += inc_y;
          //_log.log(Level.WARNING, "4: next_x:"+next_x+" next_y"+next_y);
          z += inc_z_directiony;
          if(!nLOS(x, y, (int) z, 0, inc_y, inc_z_directiony, tz, true))
            return false;
        }
      }
    }
    return true;
  }
 
  /*
   *  MoveCheck
   */
  private Location moveCheck(Location startpoint, Location destiny, int x, int y, double z, int tx, int ty, int tz)
  {
    int dx = (tx - x);
    int dy = (ty - y);
    final int distance2 = dx * dx + dy * dy;

    if(distance2 == 0)
      return destiny;
    if(distance2 > 36100) // 190*190*16 = 3040 world coord
    {
      // Avoid too long check
      // Currently we calculate a middle point
      // for wyvern users and otherwise for comfort
      double divider = Math.sqrt((double) 30000 / distance2);
      tx = x + (int) (divider * dx);
      ty = y + (int) (divider * dy);
      int dz = (tz - startpoint.getZ());
      tz = startpoint.getZ() + (int) (divider * dz);
      dx = (tx - x);
      dy = (ty - y);
      //return startpoint;
    }

    // Increment in Z coordinate when moving along X or Y axis
    // and not straight to the target. This is done because
    // calculation moves either in X or Y direction.
    final int inc_x = sign(dx);
    final int inc_y = sign(dy);
    dx = Math.abs(dx);
    dy = Math.abs(dy);

    //gm.sendMessage("MoveCheck: from X: "+x+ "Y: "+y+ "--->> X: "+tx+" Y: "+ty);

    // next_* are used in NcanMoveNext check from x,y
    int next_x = x;
    int next_y = y;
    double tempz = z;

    // creates path to the target, using only x or y direction
    // calculation stops when next_* == target
    if(dx >= dy)// dy/dx <= 1
    {
      int delta_A = 2 * dy;
      int d = delta_A - dx;
      int delta_B = delta_A - 2 * dx;

      for(int i = 0; i < dx; i++)
      {
        x = next_x;
        y = next_y;
        if(d > 0)
        {
          d += delta_B;
          next_x += inc_x;
          tempz = nCanMoveNext(x, y, (int) z, next_x, next_y, tz);
          if(tempz == Double.MIN_VALUE)
            return new Location((x << 4) + L2World.MAP_MIN_X, (y << 4) + L2World.MAP_MIN_Y, (int) z);
          z = tempz;
          next_y += inc_y;
          //_log.warning("2: next_x:"+next_x+" next_y"+next_y);
          tempz = nCanMoveNext(next_x, y, (int) z, next_x, next_y, tz);
          if(tempz == Double.MIN_VALUE)
            return new Location((x << 4) + L2World.MAP_MIN_X, (y << 4) + L2World.MAP_MIN_Y, (int) z);
          z = tempz;
        }
        else
        {
          d += delta_A;
          next_x += inc_x;
          //_log.warning("3: next_x:"+next_x+" next_y"+next_y);
          tempz = nCanMoveNext(x, y, (int) z, next_x, next_y, tz);
          if(tempz == Double.MIN_VALUE)
            return new Location((x << 4) + L2World.MAP_MIN_X, (y << 4) + L2World.MAP_MIN_Y, (int) z);
          z = tempz;
        }
      }
    }
    else
    {
      int delta_A = 2 * dx;
      int d = delta_A - dy;
      int delta_B = delta_A - 2 * dy;
      for(int i = 0; i < dy; i++)
      {
        x = next_x;
        y = next_y;
        if(d > 0)
        {
          d += delta_B;
          next_y += inc_y;
          tempz = nCanMoveNext(x, y, (int) z, next_x, next_y, tz);
          if(tempz == Double.MIN_VALUE)
            return new Location((x << 4) + L2World.MAP_MIN_X, (y << 4) + L2World.MAP_MIN_Y, (int) z);
          z = tempz;
          next_x += inc_x;
          //_log.warning("5: next_x:"+next_x+" next_y"+next_y);
          tempz = nCanMoveNext(x, next_y, (int) z, next_x, next_y, tz);
          if(tempz == Double.MIN_VALUE)
            return new Location((x << 4) + L2World.MAP_MIN_X, (y << 4) + L2World.MAP_MIN_Y, (int) z);
          z = tempz;
        }
        else
        {
          d += delta_A;
          next_y += inc_y;
          //_log.warning("6: next_x:"+next_x+" next_y"+next_y);
          tempz = nCanMoveNext(x, y, (int) z, next_x, next_y, tz);
          if(tempz == Double.MIN_VALUE)
            return new Location((x << 4) + L2World.MAP_MIN_X, (y << 4) + L2World.MAP_MIN_Y, (int) z);
          z = tempz;
        }
      }
    }
    if(z == startpoint.getZ()) // geodata hasn't modified Z in any coordinate, i.e. doesn't exist
      return destiny;
    return new Location(destiny.getX(), destiny.getY(), (int) z);
  }

  private byte sign(int x)
  {
    if(x >= 0)
      return +1;
    return -1;
  }

  //GeoEngine
  private void nInitGeodata()
  {
    _log.info("Geo Engine: - Loading Geodata...");
    File Data = new File(Config.DATAPACK_ROOT, "data/geodata/geo_index.txt");
    if(!Data.exists())
      return;
   
    BufferedReader lnr = null;
    FileReader reader = null;
    try
    {
      reader = new FileReader(Data);
      lnr = new BufferedReader(reader);
     
      String line;
      while((line = lnr.readLine()) != null) {
       
        if(line.trim().length() == 0)
          continue;
       
        StringTokenizer st = new StringTokenizer(line, "_");
        byte rx = Byte.parseByte(st.nextToken());
        byte ry = Byte.parseByte(st.nextToken());
        loadGeodataFile(rx,ry);
      }
     
    }
    catch(Exception e)
    {
      e.printStackTrace();
    }
    finally
    {
     
      if(lnr != null)
        try
        {
          lnr.close();
        }
        catch(Exception e)
        {
          e.printStackTrace();
        }
     
      if(reader != null)
        try
        {
          reader.close();
        }
        catch(Exception e)
        {
          e.printStackTrace();
        }
    }
   
    File geo_bugs = new File(Config.DATAPACK_ROOT, "data/geodata/geo_bugs.txt");
    FileOutputStream out = null;
    try
    {
      out = new FileOutputStream(geo_bugs, true);
      _geoBugsOut = new BufferedOutputStream(out);
    }
    catch(Exception e)
    {
      e.printStackTrace();
     
    }
   
  }
 
  @Override
  public void unloadGeodata(byte rx, byte ry)
  {
    short regionoffset = (short) ((rx << 5) + ry);
    _geodataIndex.set(regionoffset, null);
    _geodata.set(regionoffset, null);
  }
 
  @Override
  public boolean loadGeodataFile(byte rx, byte ry)
  {
    boolean output = false;
    String fname = "data/geodata/" + rx + "_" + ry + ".l2j";
    short regionoffset = (short) ((rx << 5) + ry);
    _log.info("Geo Engine: - Loading: " + fname + " -> region offset: " + regionoffset + "X: " + rx + " Y: " + ry);
    File Geo = new File(Config.DATAPACK_ROOT, fname);
    int size, index = 0, block = 0, flor = 0;
    FileChannel roChannel = null;
    RandomAccessFile raf = null;
    try
    {
      // Create a read-only memory-mapped file
      raf = new RandomAccessFile(Geo, "r");
      roChannel = raf.getChannel();
      size = (int) roChannel.size();
      MappedByteBuffer geo;
      if(Config.FORCE_GEODATA) //Force O/S to Loads this buffer's content into physical memory.
        //it is not guarantee, because the underlying operating system may have paged out some of the buffer's data
        geo = roChannel.map(FileChannel.MapMode.READ_ONLY, 0, size).load();
      else
        geo = roChannel.map(FileChannel.MapMode.READ_ONLY, 0, size);
      geo.order(ByteOrder.LITTLE_ENDIAN);

      if(size > 196608)
      {
        // Indexing geo files, so we will know where each block starts
        IntBuffer indexs = IntBuffer.allocate(65536);
        while(block < 65536)
        {
          byte type = geo.get(index);
          indexs.put(block,index);
          block++;
          index++;
          if(type == 0)
            index += 2; // 1x short
          else if(type == 1)
            index += 128; // 64 x short
          else
          {
            for(int b = 0; b < 64; b++)
            {
              byte layers = geo.get(index);
              index += (layers << 1) + 1;
              if(layers > flor)
                 flor = layers;
            }
          }
        }
        _geodataIndex.set(regionoffset, indexs);
      }
      _geodata.set(regionoffset,geo);

      output = true;
      _log.info("Geo Engine: - Max Layers: " + flor + " Size: " + size + " Loaded: " + index);
    }
    catch(Exception e)
    {
      e.printStackTrace();
      _log.log(Level.WARNING,"Failed to Load GeoFile at block: " + block + "\n");
     
    }
    finally
    {
      if(roChannel != null)
        try
        {
          roChannel.close();
        }
        catch(Exception e1)
        {
          e1.printStackTrace();
        }
     
      if(raf != null)
        try
        {
          raf.close();
        }
        catch(Exception e1)
        {
          e1.printStackTrace();
        }
     
    }
    return output;
  }

  //Geodata Methods
  /**
   * @param x
   * @param y
   * @return Region Offset
   */
  private short getRegionOffset(int x, int y)
  {
    int rx = x >> 11; // =/(256 * 8)
    int ry = y >> 11;
    return (short)(((rx+16) << 5) + (ry+10));
  }

  /**
   * @param geo_pos
   * @return Block Index: 0-255
   */
  private int getBlock(int geo_pos)
  {
    return (geo_pos >> 3) % 256;
  }

  /**
   * @param geo_pos
   * @return Cell Index: 0-7
   */
  private int getCell(int geo_pos)
  {
    return geo_pos % 8;
  }

  //Geodata Functions

  /**
   * @param x
   * @param y
   * @return Type of geo_block: 0-2
   */
  private short nGetType(int x, int y)
  {
    short region = getRegionOffset(x, y);
    int blockX = getBlock(x);
    int blockY = getBlock(y);
    int index = 0;
    //Geodata without index - it is just empty so index can be calculated on the fly
    if(_geodataIndex.get(region) == null)
      index = ((blockX << 8) + blockY) * 3;
    //Get Index for current block of current geodata region
    else
      index = _geodataIndex.get(region).get((blockX << 8) + blockY);
    //Buffer that Contains current Region GeoData
    ByteBuffer geo = _geodata.get(region);
    if(geo == null)
    {
      if(Config.DEBUG)
        _log.log(Level.WARNING,"Geo Region - Region Offset: " + region + " dosnt exist!!");
      return 0;
    }
    return geo.get(index);
  }
  /**
   * @param geox
   * @param geoy
   * @param z
   * @return Nearest Z
   */
  private short nGetHeight(int geox, int geoy, int z)
  {
    short region = getRegionOffset(geox, geoy);
    int blockX = getBlock(geox);
    int blockY = getBlock(geoy);
    int cellX, cellY, index;
    //Geodata without index - it is just empty so index can be calculated on the fly
    if(_geodataIndex.get(region) == null)
      index = ((blockX << 8) + blockY) * 3;
    //Get Index for current block of current region geodata
    else
      index = _geodataIndex.get(region).get((blockX << 8) + blockY);
    //Buffer that Contains current Region GeoData
    ByteBuffer geo = _geodata.get(region);
    if(geo == null)
    {
      if(Config.DEBUG)
        _log.log(Level.WARNING,"Geo Region - Region Offset: " + region + " dosnt exist!!");
      return (short) z;
    }
    //Read current block type: 0-flat,1-complex,2-multilevel
    byte type = geo.get(index);
    index++;
    if(type == 0)//flat
      return geo.getShort(index);
    else if(type == 1)//complex
    {
      cellX = getCell(geox);
      cellY = getCell(geoy);
      index += ((cellX << 3) + cellY) << 1;
      short height = geo.getShort(index);
      height = (short) (height & 0x0fff0);
      height = (short) (height >> 1); //height / 2
      return height;
    }
    else //multilevel
    {
      cellX = getCell(geox);
      cellY = getCell(geoy);
      int offset = (cellX << 3) + cellY;
      while(offset > 0)
      {
        byte lc = geo.get(index);
        index += (lc << 1) + 1;
        offset--;
      }
      byte layers = geo.get(index);
      index++;
      short height = -1;
      if(layers <= 0 || layers > 125)
      {
        if(Config.DEBUG)
          _log.log(Level.WARNING,"Broken geofile (case1), region: " + region + " - invalid layer count: " + layers + " at: " + geox + " " + geoy);
        return (short) z;
      }
      short temph = Short.MIN_VALUE;
      while(layers > 0)
      {
        height = geo.getShort(index);
        height = (short) (height & 0x0fff0);
        height = (short) (height >> 1); //height / 2
        if((z-temph) * (z-temph) > (z-height) * (z-height))
          temph = height;
        layers--;
        index += 2;
      }
      return temph;
    }
  }
 
  /**
   * @param geox
   * @param geoy
   * @param z
   * @return One layer higher Z than parameter Z
   */
  private short nGetUpperHeight(int geox, int geoy, int z)
  {
    short region = getRegionOffset(geox, geoy);
    int blockX = getBlock(geox);
    int blockY = getBlock(geoy);
    int cellX, cellY, index;
    //Geodata without index - it is just empty so index can be calculated on the fly
    if(_geodataIndex.get(region) == null)
      index = ((blockX << 8) + blockY) * 3;
    //Get Index for current block of current region geodata
    else
      index = _geodataIndex.get(region).get((blockX << 8) + blockY);
    //Buffer that Contains current Region GeoData
    ByteBuffer geo = _geodata.get(region);
    if(geo == null)
    {
      if(Config.DEBUG)
        _log.log(Level.WARNING,"Geo Region - Region Offset: " + region + " dosnt exist!!");
      return (short) z;
    }
    //Read current block type: 0-flat,1-complex,2-multilevel
    byte type = geo.get(index);
    index++;
    if(type == 0)//flat
      return geo.getShort(index);
    else if(type == 1)//complex
    {
      cellX = getCell(geox);
      cellY = getCell(geoy);
      index += ((cellX << 3) + cellY) << 1;
      short height = geo.getShort(index);
      height = (short) (height & 0x0fff0);
      height = (short) (height >> 1); //height / 2
      return height;
    }
    else //multilevel
    {
      cellX = getCell(geox);
      cellY = getCell(geoy);
      int offset = (cellX << 3) + cellY;
      while(offset > 0)
      {
        byte lc = geo.get(index);
        index += (lc << 1) + 1;
        offset--;
      }
      byte layers = geo.get(index);
      index++;
      short height =- 1;
      if(layers <= 0 || layers > 125)
      {
        if(Config.DEBUG)
          _log.log(Level.WARNING,"Broken geofile (case1), region: " + region + " - invalid layer count: " + layers + " at: " + geox + " " + geoy);
        return (short) z;
      }
      short temph = Short.MAX_VALUE;
      while(layers > 0) // from higher to lower
      {
        height = geo.getShort(index);
        height = (short) (height & 0x0fff0);
        height = (short) (height >> 1); //height / 2
        if(height < z)
          return temph;
        temph = height;
        layers--;
        index += 2;
      }
      return temph;
    }
  }
 
  /**
   * @param geox
   * @param geoy
   * @param zmin
   * @param zmax
   * @param spawnid
   * @return Z betwen zmin and zmax
   */
  private short nGetSpawnHeight(int geox, int geoy, int zmin, int zmax, int spawnid)
  {
    short region = getRegionOffset(geox, geoy);
    int blockX = getBlock(geox);
    int blockY = getBlock(geoy);
    int cellX, cellY, index;
    short temph = Short.MIN_VALUE;
    //Geodata without index - it is just empty so index can be calculated on the fly
    if(_geodataIndex.get(region) == null)
      index = ((blockX << 8) + blockY) * 3;
    //Get Index for current block of current region geodata
    else
      index = _geodataIndex.get(region).get((blockX << 8) + blockY);
    //Buffer that Contains current Region GeoData
    ByteBuffer geo = _geodata.get(region);
    if(geo == null)
    {
      if(Config.DEBUG)
        _log.log(Level.WARNING,"Geo Region - Region Offset: " + region + " dosnt exist!!");
      return (short) zmin;
    }
    //Read current block type: 0-flat,1-complex,2-multilevel
    byte type = geo.get(index);
    index++;
    if(type == 0)//flat
      temph = geo.getShort(index);
    else if(type == 1)//complex
    {
      cellX = getCell(geox);
      cellY = getCell(geoy);
      index += ((cellX << 3) + cellY) << 1;
      short height = geo.getShort(index);
      height = (short) (height & 0x0fff0);
      height = (short) (height >> 1); //height / 2
      temph = height;
    }
    else//multilevel
    {
      cellX = getCell(geox);
      cellY = getCell(geoy);
      short height;
      int offset = (cellX << 3) + cellY;
      while(offset > 0)
      {
        byte lc = geo.get(index);
        index += (lc << 1) + 1;
        offset--;
      }
      //Read current block type: 0-flat,1-complex,2-multilevel
      byte layers = geo.get(index);
      index++;
      if(layers <= 0 || layers > 125)
      {
        if(Config.DEBUG)
          _log.log(Level.WARNING,"Broken geofile (case2), region: " + region + " - invalid layer count: " + layers + " at: " + geox + " " + geoy);
        return (short) zmin;
      }
      while(layers > 0)
      {
        height = geo.getShort(index);
        height = (short) (height & 0x0fff0);
        height = (short) (height >> 1); //height / 2
        if((zmin-temph) * (zmin-temph) > (zmin-height) * (zmin-height))
          temph = height;
        layers--;
        index += 2;
      }
      if(temph > zmax + 200 || temph < zmin - 200)
      {
        if(Config.DEBUG)
          _log.log(Level.WARNING,"SpawnHeight Error - Couldnt find correct layer to spawn NPC - GeoData or Spawnlist Bug!: zmin: " + zmin + " zmax: " + zmax + " value: " + temph + " SpawnId: " + spawnid + " at: " + geox + " : " + geoy);
        return (short) zmin;
      }
    }
    if(temph > zmax + 1000 || temph < zmin - 1000)
    {
      if(Config.DEBUG)
        _log.log(Level.WARNING,"SpawnHeight Error - Spawnlist z value is wrong or GeoData error: zmin: " + zmin + " zmax: " + zmax + " value: " + temph + " SpawnId: " + spawnid + " at: " + geox + " : " + geoy);
      return (short) zmin;
    }
    return temph;
  }
  /**
   * @param x
   * @param y
   * @param z
   * @param tx
   * @param ty
   * @param tz
   * @return True if char can move to (tx,ty,tz)
   */
  private double nCanMoveNext(int x, int y, int z, int tx, int ty, int tz)
  {
    short region = getRegionOffset(x, y);
    int blockX = getBlock(x);
    int blockY = getBlock(y);
    int cellX, cellY;
    short NSWE = 0;

    int index = 0;
    //Geodata without index - it is just empty so index can be calculated on the fly
    if(_geodataIndex.get(region) == null)
      index = ((blockX << 8) + blockY) * 3;
    //Get Index for current block of current region geodata
    else
      index = _geodataIndex.get(region).get((blockX << 8) + blockY);
    //Buffer that Contains current Region GeoData
    ByteBuffer geo = _geodata.get(region);
    if(geo == null)
    {
      if(Config.DEBUG)
        _log.log(Level.WARNING,"Geo Region - Region Offset: " + region + " dosnt exist!!");
      return z;
    }
    //Read current block type: 0-flat,1-complex,2-multilevel
    byte type = geo.get(index);
    index++;
    if(type == 0) //flat
      return geo.getShort(index);
    else if(type == 1) //complex
    {
      cellX = getCell(x);
      cellY = getCell(y);
      index += ((cellX << 3) + cellY) << 1;
      short height = geo.getShort(index);
      NSWE = (short) (height & 0x0F);
      height = (short) (height & 0x0fff0);
      height = (short) (height >> 1); //height / 2
      if(checkNSWE(NSWE, x, y, tx, ty))
        return height;
      return Double.MIN_VALUE;
    }
    else //multilevel, type == 2
    {
      cellX = getCell(x);
      cellY = getCell(y);
      int offset = (cellX << 3) + cellY;
      while(offset > 0) // iterates (too many times?) to get to layer count
      {
        byte lc = geo.get(index);
        index += (lc << 1) + 1;
        offset--;
      }
      byte layers = geo.get(index);
      //_log.log(Level.WARNING, "layers"+layers);
      index++;
      short height = -1;
      if(layers <= 0 || layers > 125)
      {
        if(Config.DEBUG)
          _log.log(Level.WARNING,"Broken geofile (case3), region: " + region + " - invalid layer count: " + layers + " at: " + x + " " + y);
        return z;
      }
      short tempz = Short.MIN_VALUE;
      while(layers > 0)
      {
        height = geo.getShort(index);
        height = (short) (height & 0x0fff0);
        height = (short) (height >> 1); //height / 2

        // searches the closest layer to current z coordinate
        if((z-tempz) * (z-tempz) > (z-height) * (z-height))
        {
          //layercurr = layers;
          tempz = height;
          NSWE = geo.getShort(index);
          NSWE = (short) (NSWE & 0x0F);
        }
        layers--;
        index += 2;
      }
      if(checkNSWE(NSWE, x, y, tx, ty))
        return tempz;
      return Double.MIN_VALUE;
    }
  }
 
  /**
   * @param x
   * @param y
   * @param z
   * @param inc_x
   * @param inc_y
   * @param inc_z
   * @param tz
   * @param debug
   * @return True if Char can see target
   */
  private boolean nLOS(int x, int y, int z, int inc_x, int inc_y, double inc_z, int tz, boolean debug)
  {
    short region = getRegionOffset(x, y);
    int blockX = getBlock(x);
    int blockY = getBlock(y);
    int cellX, cellY;
    short NSWE = 0;

    int index;
    //Geodata without index - it is just empty so index can be calculated on the fly
    if(_geodataIndex.get(region) == null)
      index = ((blockX << 8) + blockY) * 3;
    //Get Index for current block of current region geodata
    else
      index = _geodataIndex.get(region).get((blockX << 8) + blockY);
    //Buffer that Contains current Region GeoData
    ByteBuffer geo = _geodata.get(region);
    if(geo == null)
    {
      if(Config.DEBUG)
        _log.log(Level.WARNING,"Geo Region - Region Offset: " + region + " dosnt exist!!");
      return true;
    }
    //Read current block type: 0-flat,1-complex,2-multilevel
    byte type = geo.get(index);
    index++;
    if(type == 0) //flat, movement and sight always possible
    {
      short height = geo.getShort(index);
      if(debug)
        _log.log(Level.WARNING,"flatheight:" + height);
      if(z > height)
        return inc_z > height;
      return inc_z < height;
    }
    else if (type == 1) //complex
    {
      cellX = getCell(x);
      cellY = getCell(y);
      index += ((cellX << 3) + cellY) << 1;
      short height = geo.getShort(index);
      NSWE = (short) (height & 0x0F);
      height = (short) (height & 0x0fff0);
      height = (short) (height >> 1); //height / 2
      if(!checkNSWE(NSWE, x, y, x + inc_x, y + inc_y))
      {
        if(debug)
          _log.log(Level.WARNING,"height:" + height + " z" + z);

        return z >= nGetUpperHeight(x + inc_x, y + inc_y, height);
      }
      return true;
    }
    else //multilevel, type == 2
    {
      cellX = getCell(x);
      cellY = getCell(y);

      int offset = (cellX << 3) + cellY;
      while(offset > 0) // iterates (too many times?) to get to layer count
      {
        byte lc = geo.get(index);
        index += (lc << 1) + 1;
        offset--;
      }
      byte layers = geo.get(index);

      index++;
      short tempZ = -1;
      if(layers <= 0 || layers > 125)
      {
        if(Config.DEBUG)
          _log.log(Level.WARNING,"Broken geofile (case4), region: " + region + " - invalid layer count: " + layers + " at: " + x + " " + y);
        return false;
      }
      short upperHeight = Short.MAX_VALUE; // big positive value
      short lowerHeight = Short.MIN_VALUE; // big negative value
      byte temp_layers = layers;
      boolean highestlayer = false;
      while(temp_layers > 0) // from higher to lower
      {
        // reads tempZ for current layer, result in world z coordinate
        tempZ = geo.getShort(index);
        tempZ = (short) (tempZ & 0x0fff0);
        tempZ = (short) (tempZ >> 1); //tempZ / 2

        if(z > tempZ)
        {
          lowerHeight = tempZ;
          NSWE = geo.getShort(index);
          NSWE = (short) (NSWE & 0x0F);
          break;
        }
        highestlayer = false;
        upperHeight = tempZ;
       
        temp_layers--;
        index += 2;
      }
      if(debug)
        _log.log(Level.WARNING,"z:" + z + " x: " + cellX + " y:" + cellY + " la " + layers + " lo:" + lowerHeight + " up:" + upperHeight);
      // Check if LOS goes under a layer/floor
      // clearly under layer but not too much under
      // lowerheight here only for geodata bug checking, layers very close? maybe could be removed
      if((z - upperHeight) < -10 && (z - upperHeight) > inc_z - 10 && (z - lowerHeight) > 40)
      {
        if(debug)
          _log.log(Level.WARNING,"false, incz" + inc_z);
        return false;
      }
     
      // or there's a fence/wall ahead when we're not on highest layer
      if(!highestlayer)
      {
        //a probable wall, there's movement block and layers above you
        if(!checkNSWE(NSWE, x, y, x + inc_x, y + inc_y)) // cannot move
        {
          if(debug)
            _log.log(Level.WARNING,"block and next in x" + inc_x + " y" + inc_y + " is:" + nGetUpperHeight(x + inc_x, y + inc_y, lowerHeight));
          // check one inc_x inc_y further, for the height there
          return z >= nGetUpperHeight(x + inc_x, y + inc_y, lowerHeight);
        }
        return true;
      }
      if(!checkNSWE(NSWE, x, y, x + inc_x, y + inc_y))
      {
        // check one inc_x inc_y further, for the height there
        return z >= nGetUpperHeight(x + inc_x, y + inc_y, lowerHeight);
      }
      return true;
    }
  }
 
  /**
   * @param x
   * @param y
   * @param z
   * @return NSWE: 0-15
   */
  private short nGetNSWE(int x, int y, int z)
  {
    short region = getRegionOffset(x, y);
    int blockX = getBlock(x);
    int blockY = getBlock(y);
    int cellX, cellY;
    short NSWE = 0;

    int index = 0;
    //Geodata without index - it is just empty so index can be calculated on the fly
    if(_geodataIndex.get(region) == null)
      index = ((blockX << 8) + blockY) * 3;
    //Get Index for current block of current region geodata
    else
      index = _geodataIndex.get(region).get((blockX << 8) + blockY);
    //Buffer that Contains current Region GeoData
    ByteBuffer geo = _geodata.get(region);
    if(geo == null)
    {
      if(Config.DEBUG)
        _log.log(Level.WARNING,"Geo Region - Region Offset: " + region + " dosnt exist!!");
      return 15;
    }
    //Read current block type: 0-flat,1-complex,2-multilevel
    byte type = geo.get(index);
    index++;
    if(type == 0)//flat
      return 15;
    else if(type == 1)//complex
    {
      cellX = getCell(x);
      cellY = getCell(y);
      index += ((cellX << 3) + cellY) << 1;
      short height = geo.getShort(index);
      NSWE = (short) (height & 0x0F);
    }
    else//multilevel
    {
      cellX = getCell(x);
      cellY = getCell(y);
      int offset = (cellX << 3) + cellY;
      while(offset > 0)
      {
        byte lc = geo.get(index);
        index += (lc << 1) + 1;
        offset--;
      }
      byte layers = geo.get(index);
      index++;
      short height = -1;
      if(layers <= 0 || layers > 125)
      {
        if(Config.DEBUG)
          _log.log(Level.WARNING,"Broken geofile (case5), region: " + region + " - invalid layer count: " + layers + " at: " + x + " " + y);
        return 15;
      }
      short tempz = Short.MIN_VALUE;
      while(layers > 0)
      {
        height = geo.getShort(index);
        height = (short) (height & 0x0fff0);
        height = (short) (height >> 1); //height / 2

        if((z-tempz) * (z-tempz) > (z-height) * (z-height))
        {
          tempz = height;
          NSWE = geo.get(index);
          NSWE = (short) (NSWE & 0x0F);
        }
        layers--;
        index += 2;
      }
    }
    return NSWE;
  }
 
  /**
   * @param n
   * @return NSWE: 0-15
   */
  @Override
  public Node[] getNeighbors(Node n)
  {
    Node newNode;
    int x = n.getNodeX();
    int y = n.getNodeY();
    int parentdirection = 0;
    if(n.getParent() != null) // check for not adding parent again
    {
      if(n.getParent().getNodeX() > x) parentdirection = 1;
      if(n.getParent().getNodeX() < x) parentdirection = -1;
      if(n.getParent().getNodeY() > y) parentdirection = 2;
      if(n.getParent().getNodeY() < y) parentdirection = -2;
    }
    short z = n.getZ();
    short region = getRegionOffset(x, y);
    int blockX = getBlock(x);
    int blockY = getBlock(y);
    int cellX, cellY;
    short NSWE = 0;
    int index = 0;
    //Geodata without index - it is just empty so index can be calculated on the fly
    if(_geodataIndex.get(region) == null)
      index = ((blockX << 8) + blockY) * 3;
    //Get Index for current block of current region geodata
    else
      index = _geodataIndex.get(region).get((blockX << 8) + blockY);
    //Buffer that Contains current Region GeoData
    ByteBuffer geo = _geodata.get(region);
    if(geo == null)
    {
      if(Config.DEBUG)
        _log.log(Level.WARNING,"Geo Region - Region Offset: " + region + " dosnt exist!!");
      return null;
    }
   
    final Node[] Neighbors = new Node[4];
    int arrayIndex = 0;
   
    //Read current block type: 0-flat,1-complex,2-multilevel
    byte type = geo.get(index);
    index++;
    if(type == 0)//flat
    {
      short height = geo.getShort(index);
      n.setZ(height);
      if(parentdirection != 1)
      {
        newNode = CellPathFinding.getInstance().readNode(x + 1, y, height);
        //newNode.setCost(0);
        Neighbors[arrayIndex++] = newNode;
      }
      if(parentdirection != 2)
      {
        newNode = CellPathFinding.getInstance().readNode(x, y + 1, height);
        Neighbors[arrayIndex++] = newNode;
      }
      if(parentdirection != -2)
      {
        newNode = CellPathFinding.getInstance().readNode(x, y - 1, height);
        Neighbors[arrayIndex++] = newNode;
      }
      if(parentdirection != -1)
      {
        newNode = CellPathFinding.getInstance().readNode(x - 1, y, height);
        Neighbors[arrayIndex++] = newNode;
      }
    }
    else if(type == 1)//complex
    {
      cellX = getCell(x);
      cellY = getCell(y);
      index += ((cellX << 3) + cellY) << 1;
      short height = geo.getShort(index);
      NSWE = (short) (height & 0x0F);
      height = (short) (height & 0x0fff0);
      height = (short)(height >> 1); //height / 2
      n.setZ(height);
      if(NSWE != 15 && parentdirection != 0)
        return null; // no node with a block will be used
      if(parentdirection != 1 && checkNSWE(NSWE, x, y, x + 1, y))
      {
        newNode = CellPathFinding.getInstance().readNode(x + 1, y, height);
        //newNode.setCost(basecost+50);
        Neighbors[arrayIndex++] = newNode;
      }
      if(parentdirection != 2 && checkNSWE(NSWE, x, y, x, y + 1))
      {
        newNode = CellPathFinding.getInstance().readNode(x, y + 1, height);
        Neighbors[arrayIndex++] = newNode;
      }
      if(parentdirection != -2 && checkNSWE(NSWE, x, y, x, y - 1))
      {
        newNode = CellPathFinding.getInstance().readNode(x, y - 1, height);
        Neighbors[arrayIndex++] = newNode;
      }
      if(parentdirection != -1 && checkNSWE(NSWE, x, y, x - 1, y))
      {
        newNode = CellPathFinding.getInstance().readNode(x-1, y, height);
        Neighbors[arrayIndex++] = newNode;
      }
    }
    else//multilevel
    {
      cellX = getCell(x);
      cellY = getCell(y);
      int offset = (cellX << 3) + cellY;
      while(offset > 0)
      {
        byte lc = geo.get(index);
        index += (lc << 1) + 1;
        offset--;
      }
      byte layers = geo.get(index);
      index++;
      short height = -1;
      if(layers <= 0 || layers > 125)
      {
        if(Config.DEBUG)
          _log.log(Level.WARNING,"Broken geofile (case5), region: " + region + " - invalid layer count: " + layers + " at: " + x + " " + y);
        return null;
      }
      short tempz = Short.MIN_VALUE;
      while(layers > 0)
      {
        height = geo.getShort(index);
        height = (short) (height & 0x0fff0);
        height = (short) (height >> 1); //height / 2

        if((z-tempz) * (z-tempz) > (z-height) * (z-height))
        {
          tempz = height;
          NSWE = geo.get(index);
          NSWE = (short) (NSWE & 0x0F);
        }
        layers--;
        index += 2;
      }
      n.setZ(tempz);
      if(NSWE != 15 && parentdirection != 0)
        return null; // no node with a block will be used
      if(parentdirection != 1 && checkNSWE(NSWE, x, y, x + 1, y))
      {
        newNode = CellPathFinding.getInstance().readNode(x+1,y,tempz);
        //newNode.setCost(basecost+50);
        Neighbors[arrayIndex++] = newNode;
      }
      if(parentdirection != 2 && checkNSWE(NSWE, x, y, x, y + 1))
      {
        newNode = CellPathFinding.getInstance().readNode(x, y + 1, tempz);
        Neighbors[arrayIndex++] = newNode;
      }
      if(parentdirection != -2 && checkNSWE(NSWE, x, y, x, y - 1))
      {
        newNode = CellPathFinding.getInstance().readNode(x, y - 1, tempz);
        Neighbors[arrayIndex++] = newNode;
      }
      if(parentdirection != -1 && checkNSWE(NSWE, x, y, x - 1, y))
      {
        newNode = CellPathFinding.getInstance().readNode(x - 1, y, tempz);
        Neighbors[arrayIndex++] = newNode;
      }
    }
   
    return L2Arrays.compact(Neighbors);
  }

  /**
   * @param NSWE
   * @param x
   * @param y
   * @param tx
   * @param ty
   * @return True if NSWE dont block given direction
   */
  private boolean checkNSWE(short NSWE, int x, int y, int tx, int ty)
  {
    //Check NSWE
    if(NSWE == 15)
       return true;
    if(tx > x)//E
    {
      if((NSWE & _e) == 0)
        return false;
    }
    else if(tx < x)//W
    {
      if((NSWE & _w) == 0)
        return false;
    }
    if(ty > y)//S
    {
      if((NSWE & _s) == 0)
        return false;
    }
    else if(ty < y)//N
    {
      if((NSWE & _n) == 0)
        return false;
    }
    return true;
  }
}
TOP

Related Classes of com.l2jfrozen.gameserver.geo.GeoEngine$SingletonHolder

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.