GeoEngine.java 43 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320
  1. /*
  2. * This program is free software: you can redistribute it and/or modify it under
  3. * the terms of the GNU General Public License as published by the Free Software
  4. * Foundation, either version 3 of the License, or (at your option) any later
  5. * version.
  6. *
  7. * This program is distributed in the hope that it will be useful, but WITHOUT
  8. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  9. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  10. * details.
  11. *
  12. * You should have received a copy of the GNU General Public License along with
  13. * this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. package net.sf.l2j.gameserver;
  16. import java.io.BufferedOutputStream;
  17. import java.io.BufferedReader;
  18. import java.io.File;
  19. import java.io.FileOutputStream;
  20. import java.io.FileReader;
  21. import java.io.LineNumberReader;
  22. import java.io.RandomAccessFile;
  23. import java.nio.ByteBuffer;
  24. import java.nio.ByteOrder;
  25. import java.nio.IntBuffer;
  26. import java.nio.MappedByteBuffer;
  27. import java.nio.channels.FileChannel;
  28. import java.util.Map;
  29. import java.util.StringTokenizer;
  30. import java.util.logging.Logger;
  31. import javolution.util.FastMap;
  32. import net.sf.l2j.Config;
  33. import net.sf.l2j.gameserver.datatables.DoorTable;
  34. import net.sf.l2j.gameserver.model.L2Object;
  35. import net.sf.l2j.gameserver.model.L2World;
  36. import net.sf.l2j.gameserver.model.Location;
  37. import net.sf.l2j.gameserver.model.actor.instance.L2DoorInstance;
  38. import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
  39. import net.sf.l2j.gameserver.model.actor.instance.L2SiegeGuardInstance;
  40. import net.sf.l2j.util.Point3D;
  41. /**
  42. *
  43. * @author -Nemesiss-
  44. */
  45. public class GeoEngine extends GeoData
  46. {
  47. private static Logger _log = Logger.getLogger(GeoData.class.getName());
  48. private static GeoEngine _instance;
  49. private final static byte _e = 1;
  50. private final static byte _w = 2;
  51. private final static byte _s = 4;
  52. private final static byte _n = 8;
  53. private static Map<Short, MappedByteBuffer> _geodata = new FastMap<Short, MappedByteBuffer>();
  54. private static Map<Short, IntBuffer> _geodataIndex = new FastMap<Short, IntBuffer>();
  55. private static BufferedOutputStream _geoBugsOut;
  56. public static GeoEngine getInstance()
  57. {
  58. if(_instance == null)
  59. _instance = new GeoEngine();
  60. return _instance;
  61. }
  62. public GeoEngine()
  63. {
  64. nInitGeodata();
  65. }
  66. //Public Methods
  67. /**
  68. * @see net.sf.l2j.gameserver.GeoData#getType(int, int)
  69. */
  70. @Override
  71. public short getType(int x, int y)
  72. {
  73. return nGetType((x - L2World.MAP_MIN_X) >> 4, (y - L2World.MAP_MIN_Y) >> 4);
  74. }
  75. /**
  76. * @see net.sf.l2j.gameserver.GeoData#getHeight(int, int, int)
  77. */
  78. @Override
  79. public short getHeight(int x, int y, int z)
  80. {
  81. return nGetHeight((x - L2World.MAP_MIN_X) >> 4,(y - L2World.MAP_MIN_Y) >> 4,z);
  82. }
  83. /**
  84. * @see net.sf.l2j.gameserver.GeoData#getSpawnHeight(int, int, int, int, int)
  85. */
  86. @Override
  87. public short getSpawnHeight(int x, int y, int zmin, int zmax, int spawnid)
  88. {
  89. return nGetSpawnHeight((x - L2World.MAP_MIN_X) >> 4,(y - L2World.MAP_MIN_Y) >> 4,zmin,zmax,spawnid);
  90. }
  91. /**
  92. * @see net.sf.l2j.gameserver.GeoData#geoPosition(int, int)
  93. */
  94. @Override
  95. public String geoPosition(int x, int y)
  96. {
  97. int gx = (x - L2World.MAP_MIN_X) >> 4;
  98. int gy = (y - L2World.MAP_MIN_Y) >> 4;
  99. return "bx: "+getBlock(gx)+" by: "+getBlock(gy)+" cx: "+getCell(gx)+" cy: "+getCell(gy)+" region offset: "+getRegionOffset(gx,gy);
  100. }
  101. /**
  102. * @see net.sf.l2j.gameserver.GeoData#canSeeTarget(L2Object, Point3D)
  103. */
  104. @Override
  105. public boolean canSeeTarget(L2Object cha, Point3D target)
  106. {
  107. if (DoorTable.getInstance().checkIfDoorsBetween(cha.getX(),cha.getY(),cha.getZ(),target.getX(),target.getY(),target.getZ()))
  108. return false;
  109. if(cha.getZ() >= target.getZ())
  110. return canSeeTarget(cha.getX(),cha.getY(),cha.getZ(),target.getX(),target.getY(),target.getZ());
  111. else
  112. return canSeeTarget(target.getX(),target.getY(),target.getZ(), cha.getX(),cha.getY(),cha.getZ());
  113. }
  114. /**
  115. * @see net.sf.l2j.gameserver.GeoData#canSeeTarget(net.sf.l2j.gameserver.model.L2Object, net.sf.l2j.gameserver.model.L2Object)
  116. */
  117. @Override
  118. public boolean canSeeTarget(L2Object cha, L2Object target)
  119. {
  120. // To be able to see over fences and give the player the viewpoint
  121. // game client has, all coordinates are lifted 45 from ground.
  122. // Because of layer selection in LOS algorithm (it selects -45 there
  123. // and some layers can be very close...) do not change this without
  124. // changing the LOS code.
  125. // Basically the +45 is character height. Raid bosses are naturally higher,
  126. // dwarves shorter, but this should work relatively well.
  127. // If this is going to be improved, use e.g.
  128. // ((L2Character)cha).getTemplate().collisionHeight
  129. int z = cha.getZ()+45;
  130. if(cha instanceof L2SiegeGuardInstance) z += 30; // well they don't move closer to balcony fence at the moment :(
  131. int z2 = target.getZ()+45;
  132. if (!(target instanceof L2DoorInstance)
  133. && DoorTable.getInstance().checkIfDoorsBetween(cha.getX(),cha.getY(),z,target.getX(),target.getY(),z2))
  134. return false;
  135. if(target instanceof L2DoorInstance) return true; // door coordinates are hinge coords..
  136. if(target instanceof L2SiegeGuardInstance) z2 += 30; // well they don't move closer to balcony fence at the moment :(
  137. if(cha.getZ() >= target.getZ())
  138. return canSeeTarget(cha.getX(),cha.getY(),z,target.getX(),target.getY(),z2);
  139. else
  140. return canSeeTarget(target.getX(),target.getY(),z2, cha.getX(),cha.getY(),z);
  141. }
  142. /**
  143. * @see net.sf.l2j.gameserver.GeoData#canSeeTargetDebug(net.sf.l2j.gameserver.model.actor.instance.L2PcInstance, net.sf.l2j.gameserver.model.L2Object)
  144. */
  145. @Override
  146. public boolean canSeeTargetDebug(L2PcInstance gm, L2Object target)
  147. {
  148. // comments: see above
  149. int z = gm.getZ()+45;
  150. int z2 = target.getZ()+45;
  151. if(target instanceof L2DoorInstance)
  152. {
  153. gm.sendMessage("door always true");
  154. return true; // door coordinates are hinge coords..
  155. }
  156. if(gm.getZ() >= target.getZ())
  157. 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);
  158. else
  159. 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);
  160. }
  161. /**
  162. * @see net.sf.l2j.gameserver.GeoData#getNSWE(int, int, int)
  163. */
  164. @Override
  165. public short getNSWE(int x, int y, int z)
  166. {
  167. return nGetNSWE((x - L2World.MAP_MIN_X) >> 4,(y - L2World.MAP_MIN_Y) >> 4,z);
  168. }
  169. /**
  170. * @see net.sf.l2j.gameserver.GeoData#moveCheck(int, int, int, int, int, int)
  171. */
  172. @Override
  173. public Location moveCheck(int x, int y, int z, int tx, int ty, int tz)
  174. {
  175. Location startpoint = new Location(x,y,z);
  176. if (DoorTable.getInstance().checkIfDoorsBetween(x,y,z,tx,ty,tz))
  177. return startpoint;
  178. Location destiny = new Location(tx,ty,tz);
  179. 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);
  180. }
  181. /**
  182. * @see net.sf.l2j.gameserver.GeoData#addGeoDataBug(net.sf.l2j.gameserver.model.actor.instance.L2PcInstance, java.lang.String)
  183. */
  184. @Override
  185. public void addGeoDataBug(L2PcInstance gm, String comment)
  186. {
  187. int gx = (gm.getX() - L2World.MAP_MIN_X) >> 4;
  188. int gy = (gm.getY() - L2World.MAP_MIN_Y) >> 4;
  189. int bx = getBlock(gx);
  190. int by = getBlock(gy);
  191. int cx = getCell(gx);
  192. int cy = getCell(gy);
  193. int rx = (gx >> 11) + 16;
  194. int ry = (gy >> 11) + 10;
  195. String out = rx+";"+ry+";"+bx+";"+by+";"+cx+";"+cy+";"+gm.getZ()+";"+comment+"\n";
  196. try
  197. {
  198. _geoBugsOut.write(out.getBytes());
  199. _geoBugsOut.flush();
  200. gm.sendMessage("GeoData bug saved!");
  201. } catch (Exception e) {
  202. e.printStackTrace();
  203. gm.sendMessage("GeoData bug save Failed!");
  204. }
  205. }
  206. // Private Methods
  207. private boolean canSeeTarget(int x, int y, int z, int tx, int ty, int tz)
  208. {
  209. 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);
  210. }
  211. public boolean hasGeo(int x, int y)
  212. {
  213. int gx = (x - L2World.MAP_MIN_X) >> 4;
  214. int gy = (y - L2World.MAP_MIN_Y) >> 4;
  215. short region = getRegionOffset(gx,gy);
  216. if (_geodata.get(region) != null)
  217. return false;
  218. return true;
  219. }
  220. private static boolean canSee(int x, int y, double z, int tx, int ty, int tz)
  221. {
  222. int dx = (tx - x);
  223. int dy = (ty - y);
  224. final double dz = (tz - z);
  225. final int distance2 = dx*dx+dy*dy;
  226. if (distance2 > 90000) // (300*300) 300*16 = 4800 in world coord
  227. {
  228. //Avoid too long check
  229. return false;
  230. }
  231. // very short checks: 9 => 144 world distance
  232. // this ensures NLOS function has enough points to calculate,
  233. // it might not work when distance is small and path vertical
  234. else if (distance2 < 82)
  235. {
  236. // 150 should be too deep/high.
  237. if(dz*dz > 22500)
  238. {
  239. short region = getRegionOffset(x,y);
  240. // geodata is loaded for region and mobs should have correct Z coordinate...
  241. // so there would likely be a floor in between the two
  242. if (_geodata.get(region) != null)
  243. return false;
  244. }
  245. return true;
  246. }
  247. // Increment in Z coordinate when moving along X or Y axis
  248. // and not straight to the target. This is done because
  249. // calculation moves either in X or Y direction.
  250. final int inc_x = sign(dx);
  251. final int inc_y = sign(dy);
  252. dx = Math.abs(dx);
  253. dy = Math.abs(dy);
  254. final double inc_z_directionx = dz*dx / (distance2);
  255. final double inc_z_directiony = dz*dy / (distance2);
  256. // next_* are used in NLOS check from x,y
  257. int next_x = x;
  258. int next_y = y;
  259. // creates path to the target
  260. // calculation stops when next_* == target
  261. if (dx >= dy)// dy/dx <= 1
  262. {
  263. int delta_A = 2*dy;
  264. int d = delta_A - dx;
  265. int delta_B = delta_A - 2*dx;
  266. for (int i = 0; i < dx; i++)
  267. {
  268. x = next_x;
  269. y = next_y;
  270. if (d > 0)
  271. {
  272. d += delta_B;
  273. next_x += inc_x;
  274. z += inc_z_directionx;
  275. next_y += inc_y;
  276. z += inc_z_directiony;
  277. //_log.warning("1: next_x:"+next_x+" next_y"+next_y);
  278. if (!nLOS(x,y,(int)z,inc_x,inc_y,inc_z_directionx+inc_z_directiony,tz,false))
  279. return false;
  280. }
  281. else
  282. {
  283. d += delta_A;
  284. next_x += inc_x;
  285. //_log.warning("2: next_x:"+next_x+" next_y"+next_y);
  286. z += inc_z_directionx;
  287. if (!nLOS(x,y,(int)z,inc_x,0,inc_z_directionx,tz,false))
  288. return false;
  289. }
  290. }
  291. }
  292. else
  293. {
  294. int delta_A = 2*dx;
  295. int d = delta_A - dy;
  296. int delta_B = delta_A - 2*dy;
  297. for (int i = 0; i < dy; i++)
  298. {
  299. x = next_x;
  300. y = next_y;
  301. if (d > 0)
  302. {
  303. d += delta_B;
  304. next_y += inc_y;
  305. z += inc_z_directiony;
  306. next_x += inc_x;
  307. z += inc_z_directionx;
  308. //_log.warning("3: next_x:"+next_x+" next_y"+next_y);
  309. if (!nLOS(x,y,(int)z,inc_x,inc_y,inc_z_directionx+inc_z_directiony,tz,false))
  310. return false;
  311. }
  312. else
  313. {
  314. d += delta_A;
  315. next_y += inc_y;
  316. //_log.warning("4: next_x:"+next_x+" next_y"+next_y);
  317. z += inc_z_directiony;
  318. if (!nLOS(x,y,(int)z,0,inc_y,inc_z_directiony,tz,false))
  319. return false;
  320. }
  321. }
  322. }
  323. return true;
  324. }
  325. /*
  326. * Debug function for checking if there's a line of sight between
  327. * two coordinates.
  328. *
  329. * Creates points for line of sight check (x,y,z towards target) and
  330. * in each point, layer and movement checks are made with NLOS function.
  331. *
  332. * Coordinates here are geodata x,y but z coordinate is world coordinate
  333. */
  334. private static boolean canSeeDebug(L2PcInstance gm, int x, int y, double z, int tx, int ty, int tz)
  335. {
  336. int dx = (tx - x);
  337. int dy = (ty - y);
  338. final double dz = (tz - z);
  339. final int distance2 = dx*dx+dy*dy;
  340. if (distance2 > 90000) // (300*300) 300*16 = 4800 in world coord
  341. {
  342. //Avoid too long check
  343. gm.sendMessage("dist > 300");
  344. return false;
  345. }
  346. // very short checks: 9 => 144 world distance
  347. // this ensures NLOS function has enough points to calculate,
  348. // it might not work when distance is small and path vertical
  349. else if (distance2 < 82)
  350. {
  351. // 150 should be too deep/high.
  352. if(dz*dz > 22500)
  353. {
  354. short region = getRegionOffset(x,y);
  355. // geodata is loaded for region and mobs should have correct Z coordinate...
  356. // so there would likely be a floor in between the two
  357. if (_geodata.get(region) != null)
  358. return false;
  359. }
  360. return true;
  361. }
  362. // Increment in Z coordinate when moving along X or Y axis
  363. // and not straight to the target. This is done because
  364. // calculation moves either in X or Y direction.
  365. final int inc_x = sign(dx);
  366. final int inc_y = sign(dy);
  367. dx = Math.abs(dx);
  368. dy = Math.abs(dy);
  369. final double inc_z_directionx = dz*dx / (distance2);
  370. final double inc_z_directiony = dz*dy / (distance2);
  371. gm.sendMessage("Los: from X: "+x+ "Y: "+y+ "--->> X: "+tx+" Y: "+ty);
  372. // next_* are used in NLOS check from x,y
  373. int next_x = x;
  374. int next_y = y;
  375. // creates path to the target
  376. // calculation stops when next_* == target
  377. if (dx >= dy)// dy/dx <= 1
  378. {
  379. int delta_A = 2*dy;
  380. int d = delta_A - dx;
  381. int delta_B = delta_A - 2*dx;
  382. for (int i = 0; i < dx; i++)
  383. {
  384. x = next_x;
  385. y = next_y;
  386. if (d > 0)
  387. {
  388. d += delta_B;
  389. next_x += inc_x;
  390. z += inc_z_directionx;
  391. next_y += inc_y;
  392. z += inc_z_directiony;
  393. //_log.warning("1: next_x:"+next_x+" next_y"+next_y);
  394. if (!nLOS(x,y,(int)z,inc_x,inc_y,inc_z_directionx+inc_z_directiony,tz,true))
  395. return false;
  396. }
  397. else
  398. {
  399. d += delta_A;
  400. next_x += inc_x;
  401. //_log.warning("2: next_x:"+next_x+" next_y"+next_y);
  402. z += inc_z_directionx;
  403. if (!nLOS(x,y,(int)z,inc_x,0,inc_z_directionx,tz,true))
  404. return false;
  405. }
  406. }
  407. }
  408. else
  409. {
  410. int delta_A = 2*dx;
  411. int d = delta_A - dy;
  412. int delta_B = delta_A - 2*dy;
  413. for (int i = 0; i < dy; i++)
  414. {
  415. x = next_x;
  416. y = next_y;
  417. if (d > 0)
  418. {
  419. d += delta_B;
  420. next_y += inc_y;
  421. z += inc_z_directiony;
  422. next_x += inc_x;
  423. z += inc_z_directionx;
  424. //_log.warning("3: next_x:"+next_x+" next_y"+next_y);
  425. if (!nLOS(x,y,(int)z,inc_x,inc_y,inc_z_directionx+inc_z_directiony,tz,true))
  426. return false;
  427. }
  428. else
  429. {
  430. d += delta_A;
  431. next_y += inc_y;
  432. //_log.warning("4: next_x:"+next_x+" next_y"+next_y);
  433. z += inc_z_directiony;
  434. if (!nLOS(x,y,(int)z,0,inc_y,inc_z_directiony,tz,true))
  435. return false;
  436. }
  437. }
  438. }
  439. return true;
  440. }
  441. /*
  442. * MoveCheck
  443. */
  444. private static Location moveCheck(Location startpoint, Location destiny, int x, int y, double z, int tx, int ty, int tz)
  445. {
  446. int dx = (tx - x);
  447. int dy = (ty - y);
  448. final int distance2 = dx*dx+dy*dy;
  449. if (distance2 == 0)
  450. return destiny;
  451. if (distance2 > 36100) // 190*190*16 = 3040 world coord
  452. {
  453. // Avoid too long check
  454. // Currently we calculate a middle point
  455. // for wyvern users and otherwise for comfort
  456. double divider = Math.sqrt((double)30000/distance2);
  457. tx = x + (int)(divider * dx);
  458. ty = y + (int)(divider * dy);
  459. int dz = (tz - startpoint.getZ());
  460. tz = startpoint.getZ() + (int)(divider * dz);
  461. dx = (tx - x);
  462. dy = (ty - y);
  463. //return startpoint;
  464. }
  465. // Increment in Z coordinate when moving along X or Y axis
  466. // and not straight to the target. This is done because
  467. // calculation moves either in X or Y direction.
  468. final int inc_x = sign(dx);
  469. final int inc_y = sign(dy);
  470. dx = Math.abs(dx);
  471. dy = Math.abs(dy);
  472. //gm.sendMessage("MoveCheck: from X: "+x+ "Y: "+y+ "--->> X: "+tx+" Y: "+ty);
  473. // next_* are used in NcanMoveNext check from x,y
  474. int next_x = x;
  475. int next_y = y;
  476. double tempz = z;
  477. // creates path to the target, using only x or y direction
  478. // calculation stops when next_* == target
  479. if (dx >= dy)// dy/dx <= 1
  480. {
  481. int delta_A = 2*dy;
  482. int d = delta_A - dx;
  483. int delta_B = delta_A - 2*dx;
  484. for (int i = 0; i < dx; i++)
  485. {
  486. x = next_x;
  487. y = next_y;
  488. if (d > 0)
  489. {
  490. d += delta_B;
  491. next_x += inc_x;
  492. next_y += inc_y;
  493. //_log.warning("2: next_x:"+next_x+" next_y"+next_y);
  494. tempz = nCanMoveNext(x,y,(int)z,next_x,next_y,tz);
  495. if (tempz == Double.MIN_VALUE)
  496. return new Location((x << 4) + L2World.MAP_MIN_X,(y << 4) + L2World.MAP_MIN_Y,(int)z);
  497. else z = tempz;
  498. }
  499. else
  500. {
  501. d += delta_A;
  502. next_x += inc_x;
  503. //_log.warning("3: next_x:"+next_x+" next_y"+next_y);
  504. tempz = nCanMoveNext(x,y,(int)z,next_x,next_y,tz);
  505. if (tempz == Double.MIN_VALUE)
  506. return new Location((x << 4) + L2World.MAP_MIN_X,(y << 4) + L2World.MAP_MIN_Y,(int)z);
  507. else z = tempz;
  508. }
  509. }
  510. }
  511. else
  512. {
  513. int delta_A = 2*dx;
  514. int d = delta_A - dy;
  515. int delta_B = delta_A - 2*dy;
  516. for (int i = 0; i < dy; i++)
  517. {
  518. x = next_x;
  519. y = next_y;
  520. if (d > 0)
  521. {
  522. d += delta_B;
  523. next_y += inc_y;
  524. next_x += inc_x;
  525. //_log.warning("5: next_x:"+next_x+" next_y"+next_y);
  526. tempz = nCanMoveNext(x,y,(int)z,next_x,next_y,tz);
  527. if (tempz == Double.MIN_VALUE)
  528. return new Location((x << 4) + L2World.MAP_MIN_X,(y << 4) + L2World.MAP_MIN_Y,(int)z);
  529. else z = tempz;
  530. }
  531. else
  532. {
  533. d += delta_A;
  534. next_y += inc_y;
  535. //_log.warning("6: next_x:"+next_x+" next_y"+next_y);
  536. tempz = nCanMoveNext(x,y,(int)z,next_x,next_y,tz);
  537. if (tempz == Double.MIN_VALUE)
  538. return new Location((x << 4) + L2World.MAP_MIN_X,(y << 4) + L2World.MAP_MIN_Y,(int)z);
  539. else z = tempz;
  540. }
  541. }
  542. }
  543. if (z == startpoint.getZ()) // geodata hasn't modified Z in any coordinate, i.e. doesn't exist
  544. return destiny;
  545. else
  546. return new Location(destiny.getX(),destiny.getY(),(int)z);
  547. }
  548. private static byte sign(int x)
  549. {
  550. if (x >= 0)
  551. return +1;
  552. else
  553. return -1;
  554. }
  555. //GeoEngine
  556. private static void nInitGeodata()
  557. {
  558. LineNumberReader lnr = null;
  559. try
  560. {
  561. _log.info("Geo Engine: - Loading Geodata...");
  562. File Data = new File("./data/geodata/geo_index.txt");
  563. if (!Data.exists())
  564. return;
  565. lnr = new LineNumberReader(new BufferedReader(new FileReader(Data)));
  566. } catch (Exception e) {
  567. e.printStackTrace();
  568. throw new Error("Failed to Load geo_index File.");
  569. }
  570. String line;
  571. try
  572. {
  573. while ((line = lnr.readLine()) != null) {
  574. if (line.trim().length() == 0)
  575. continue;
  576. StringTokenizer st = new StringTokenizer(line, "_");
  577. byte rx = Byte.parseByte(st.nextToken());
  578. byte ry = Byte.parseByte(st.nextToken());
  579. loadGeodataFile(rx,ry);
  580. }
  581. } catch (Exception e) {
  582. e.printStackTrace();
  583. throw new Error("Failed to Read geo_index File.");
  584. }
  585. try
  586. {
  587. File geo_bugs = new File("./data/geodata/geo_bugs.txt");
  588. _geoBugsOut = new BufferedOutputStream(new FileOutputStream(geo_bugs,true));
  589. } catch (Exception e) {
  590. e.printStackTrace();
  591. throw new Error("Failed to Load geo_bugs.txt File.");
  592. }
  593. }
  594. public static void unloadGeodata(byte rx, byte ry)
  595. {
  596. short regionoffset = (short)((rx << 5) + ry);
  597. _geodataIndex.remove(regionoffset);
  598. _geodata.remove(regionoffset);
  599. }
  600. public static boolean loadGeodataFile(byte rx, byte ry)
  601. {
  602. String fname = "./data/geodata/"+rx+"_"+ry+".l2j";
  603. short regionoffset = (short)((rx << 5) + ry);
  604. _log.info("Geo Engine: - Loading: "+fname+" -> region offset: "+regionoffset+"X: "+rx+" Y: "+ry);
  605. File Geo = new File(fname);
  606. int size, index = 0, block = 0, flor = 0;
  607. try {
  608. // Create a read-only memory-mapped file
  609. FileChannel roChannel = new RandomAccessFile(Geo, "r").getChannel();
  610. size = (int)roChannel.size();
  611. MappedByteBuffer geo;
  612. if (Config.FORCE_GEODATA) //Force O/S to Loads this buffer's content into physical memory.
  613. //it is not guarantee, because the underlying operating system may have paged out some of the buffer's data
  614. geo = roChannel.map(FileChannel.MapMode.READ_ONLY, 0, size).load();
  615. else
  616. geo = roChannel.map(FileChannel.MapMode.READ_ONLY, 0, size);
  617. geo.order(ByteOrder.LITTLE_ENDIAN);
  618. if (size > 196608)
  619. {
  620. // Indexing geo files, so we will know where each block starts
  621. IntBuffer indexs = IntBuffer.allocate(65536);
  622. while(block < 65536)
  623. {
  624. byte type = geo.get(index);
  625. indexs.put(block,index);
  626. block++;
  627. index++;
  628. if(type == 0)
  629. index += 2; // 1x short
  630. else if(type == 1)
  631. index += 128; // 64 x short
  632. else
  633. {
  634. int b;
  635. for(b=0;b<64;b++)
  636. {
  637. byte layers = geo.get(index);
  638. index += (layers << 1) + 1;
  639. if (layers > flor)
  640. flor = layers;
  641. }
  642. }
  643. }
  644. _geodataIndex.put(regionoffset, indexs);
  645. }
  646. _geodata.put(regionoffset,geo);
  647. _log.info("Geo Engine: - Max Layers: "+flor+" Size: "+size+" Loaded: "+index);
  648. } catch (Exception e)
  649. {
  650. e.printStackTrace();
  651. _log.warning("Failed to Load GeoFile at block: "+block+"\n");
  652. return false;
  653. }
  654. return true;
  655. }
  656. //Geodata Methods
  657. /**
  658. * @param x
  659. * @param y
  660. * @return Region Offset
  661. */
  662. private static short getRegionOffset(int x, int y)
  663. {
  664. int rx = x >> 11; // =/(256 * 8)
  665. int ry = y >> 11;
  666. return (short)(((rx+16) << 5) + (ry+10));
  667. }
  668. /**
  669. * @param pos
  670. * @return Block Index: 0-255
  671. */
  672. private static int getBlock(int geo_pos)
  673. {
  674. return (geo_pos >> 3) % 256;
  675. }
  676. /**
  677. * @param pos
  678. * @return Cell Index: 0-7
  679. */
  680. private static int getCell(int geo_pos)
  681. {
  682. return geo_pos % 8;
  683. }
  684. //Geodata Functions
  685. /**
  686. * @param x
  687. * @param y
  688. * @return Type of geo_block: 0-2
  689. */
  690. private static short nGetType(int x, int y)
  691. {
  692. short region = getRegionOffset(x,y);
  693. int blockX = getBlock(x);
  694. int blockY = getBlock(y);
  695. int index = 0;
  696. //Geodata without index - it is just empty so index can be calculated on the fly
  697. if(_geodataIndex.get(region) == null) index = ((blockX << 8) + blockY)*3;
  698. //Get Index for current block of current geodata region
  699. else index = _geodataIndex.get(region).get((blockX << 8) + blockY);
  700. //Buffer that Contains current Region GeoData
  701. ByteBuffer geo = _geodata.get(region);
  702. if(geo == null)
  703. {
  704. if(Config.DEBUG)
  705. _log.warning("Geo Region - Region Offset: "+region+" dosnt exist!!");
  706. return 0;
  707. }
  708. return geo.get(index);
  709. }
  710. /**
  711. * @param x
  712. * @param y
  713. * @param z
  714. * @return Nearest Z
  715. */
  716. private static short nGetHeight(int geox, int geoy, int z)
  717. {
  718. short region = getRegionOffset(geox,geoy);
  719. int blockX = getBlock(geox);
  720. int blockY = getBlock(geoy);
  721. int cellX, cellY, index;
  722. //Geodata without index - it is just empty so index can be calculated on the fly
  723. if(_geodataIndex.get(region) == null) index = ((blockX << 8) + blockY)*3;
  724. //Get Index for current block of current region geodata
  725. else index = _geodataIndex.get(region).get(((blockX << 8))+(blockY));
  726. //Buffer that Contains current Region GeoData
  727. ByteBuffer geo = _geodata.get(region);
  728. if(geo == null)
  729. {
  730. if(Config.DEBUG)
  731. _log.warning("Geo Region - Region Offset: "+region+" dosnt exist!!");
  732. return (short)z;
  733. }
  734. //Read current block type: 0-flat,1-complex,2-multilevel
  735. byte type = geo.get(index);
  736. index++;
  737. if(type == 0)//flat
  738. return geo.getShort(index);
  739. else if(type == 1)//complex
  740. {
  741. cellX = getCell(geox);
  742. cellY = getCell(geoy);
  743. index += ((cellX << 3) + cellY) << 1;
  744. short height = geo.getShort(index);
  745. height = (short)(height&0x0fff0);
  746. height = (short)(height >> 1); //height / 2
  747. return height;
  748. }
  749. else //multilevel
  750. {
  751. cellX = getCell(geox);
  752. cellY = getCell(geoy);
  753. int offset = (cellX << 3) + cellY;
  754. while(offset > 0)
  755. {
  756. byte lc = geo.get(index);
  757. index += (lc << 1) + 1;
  758. offset--;
  759. }
  760. byte layers = geo.get(index);
  761. index++;
  762. short height=-1;
  763. if(layers <= 0 || layers > 125)
  764. {
  765. _log.warning("Broken geofile (case1), region: "+region+" - invalid layer count: "+layers+" at: "+geox+" "+geoy);
  766. return (short)z;
  767. }
  768. short temph = Short.MIN_VALUE;
  769. while(layers > 0)
  770. {
  771. height = geo.getShort(index);
  772. height = (short)(height&0x0fff0);
  773. height = (short)(height >> 1); //height / 2
  774. if ((z-temph)*(z-temph) > (z-height)*(z-height))
  775. temph = height;
  776. layers--;
  777. index += 2;
  778. }
  779. return temph;
  780. }
  781. }
  782. /**
  783. * @param x
  784. * @param y
  785. * @param z
  786. * @return One layer higher Z than parameter Z
  787. */
  788. private static short nGetUpperHeight(int geox, int geoy, int z)
  789. {
  790. short region = getRegionOffset(geox,geoy);
  791. int blockX = getBlock(geox);
  792. int blockY = getBlock(geoy);
  793. int cellX, cellY, index;
  794. //Geodata without index - it is just empty so index can be calculated on the fly
  795. if(_geodataIndex.get(region) == null) index = ((blockX << 8) + blockY)*3;
  796. //Get Index for current block of current region geodata
  797. else index = _geodataIndex.get(region).get(((blockX << 8))+(blockY));
  798. //Buffer that Contains current Region GeoData
  799. ByteBuffer geo = _geodata.get(region);
  800. if(geo == null)
  801. {
  802. if(Config.DEBUG)
  803. _log.warning("Geo Region - Region Offset: "+region+" dosnt exist!!");
  804. return (short)z;
  805. }
  806. //Read current block type: 0-flat,1-complex,2-multilevel
  807. byte type = geo.get(index);
  808. index++;
  809. if(type == 0)//flat
  810. return geo.getShort(index);
  811. else if(type == 1)//complex
  812. {
  813. cellX = getCell(geox);
  814. cellY = getCell(geoy);
  815. index += ((cellX << 3) + cellY) << 1;
  816. short height = geo.getShort(index);
  817. height = (short)(height&0x0fff0);
  818. height = (short)(height >> 1); //height / 2
  819. return height;
  820. }
  821. else //multilevel
  822. {
  823. cellX = getCell(geox);
  824. cellY = getCell(geoy);
  825. int offset = (cellX << 3) + cellY;
  826. while(offset > 0)
  827. {
  828. byte lc = geo.get(index);
  829. index += (lc << 1) + 1;
  830. offset--;
  831. }
  832. byte layers = geo.get(index);
  833. index++;
  834. short height=-1;
  835. if(layers <= 0 || layers > 125)
  836. {
  837. _log.warning("Broken geofile (case1), region: "+region+" - invalid layer count: "+layers+" at: "+geox+" "+geoy);
  838. return (short)z;
  839. }
  840. short temph = Short.MAX_VALUE;
  841. while(layers > 0) // from higher to lower
  842. {
  843. height = geo.getShort(index);
  844. height = (short)(height&0x0fff0);
  845. height = (short)(height >> 1); //height / 2
  846. if (height < z) return temph;
  847. temph = height;
  848. layers--;
  849. index += 2;
  850. }
  851. return temph;
  852. }
  853. }
  854. /**
  855. * @param x
  856. * @param y
  857. * @param zmin
  858. * @param zmax
  859. * @return Z betwen zmin and zmax
  860. */
  861. private static short nGetSpawnHeight(int geox, int geoy, int zmin, int zmax, int spawnid)
  862. {
  863. short region = getRegionOffset(geox,geoy);
  864. int blockX = getBlock(geox);
  865. int blockY = getBlock(geoy);
  866. int cellX, cellY, index;
  867. short temph = Short.MIN_VALUE;
  868. //Geodata without index - it is just empty so index can be calculated on the fly
  869. if(_geodataIndex.get(region) == null) index = ((blockX << 8) + blockY)*3;
  870. //Get Index for current block of current region geodata
  871. else index = _geodataIndex.get(region).get(((blockX << 8))+(blockY));
  872. //Buffer that Contains current Region GeoData
  873. ByteBuffer geo = _geodata.get(region);
  874. if(geo == null)
  875. {
  876. if(Config.DEBUG)
  877. _log.warning("Geo Region - Region Offset: "+region+" dosnt exist!!");
  878. return (short)zmin;
  879. }
  880. //Read current block type: 0-flat,1-complex,2-multilevel
  881. byte type = geo.get(index);
  882. index++;
  883. if(type == 0)//flat
  884. temph = geo.getShort(index);
  885. else if(type == 1)//complex
  886. {
  887. cellX = getCell(geox);
  888. cellY = getCell(geoy);
  889. index += ((cellX << 3) + cellY) << 1;
  890. short height = geo.getShort(index);
  891. height = (short)(height&0x0fff0);
  892. height = (short)(height >> 1); //height / 2
  893. temph = height;
  894. }
  895. else//multilevel
  896. {
  897. cellX = getCell(geox);
  898. cellY = getCell(geoy);
  899. short height;
  900. int offset = (cellX << 3) + cellY;
  901. while(offset > 0)
  902. {
  903. byte lc = geo.get(index);
  904. index += (lc << 1) + 1;
  905. offset--;
  906. }
  907. //Read current block type: 0-flat,1-complex,2-multilevel
  908. byte layers = geo.get(index);
  909. index++;
  910. if(layers <= 0 || layers > 125)
  911. {
  912. _log.warning("Broken geofile (case2), region: "+region+" - invalid layer count: "+layers+" at: "+geox+" "+geoy);
  913. return (short)zmin;
  914. }
  915. while(layers > 0)
  916. {
  917. height = geo.getShort(index);
  918. height = (short)(height&0x0fff0);
  919. height = (short)(height >> 1); //height / 2
  920. if ((zmin-temph)*(zmin-temph) > (zmin-height)*(zmin-height))
  921. temph = height;
  922. layers--;
  923. index += 2;
  924. }
  925. if (temph > zmax + 200 || temph < zmin - 200)
  926. {
  927. if(Config.DEBUG)
  928. _log.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);
  929. return (short)zmin;
  930. }
  931. }
  932. if (temph > zmax + 1000 || temph < zmin - 1000)
  933. {
  934. if(Config.DEBUG)
  935. _log.warning("SpawnHeight Error - Spawnlist z value is wrong or GeoData error: zmin: "+zmin+" zmax: "+zmax+" value: "+temph+" SpawnId: "+spawnid+" at: "+geox+" : "+geoy);
  936. return (short)zmin;
  937. }
  938. return temph;
  939. }
  940. /**
  941. * @param x
  942. * @param y
  943. * @param z
  944. * @param tx
  945. * @param ty
  946. * @param tz
  947. * @return True if char can move to (tx,ty,tz)
  948. */
  949. private static double nCanMoveNext(int x, int y, int z, int tx, int ty, int tz)
  950. {
  951. short region = getRegionOffset(x,y);
  952. int blockX = getBlock(x);
  953. int blockY = getBlock(y);
  954. int cellX, cellY;
  955. short NSWE = 0;
  956. int index = 0;
  957. //Geodata without index - it is just empty so index can be calculated on the fly
  958. if(_geodataIndex.get(region) == null) index = ((blockX << 8) + blockY)*3;
  959. //Get Index for current block of current region geodata
  960. else index = _geodataIndex.get(region).get(((blockX << 8))+(blockY));
  961. //Buffer that Contains current Region GeoData
  962. ByteBuffer geo = _geodata.get(region);
  963. if(geo == null)
  964. {
  965. if(Config.DEBUG)
  966. _log.warning("Geo Region - Region Offset: "+region+" dosnt exist!!");
  967. return z;
  968. }
  969. //Read current block type: 0-flat,1-complex,2-multilevel
  970. byte type = geo.get(index);
  971. index++;
  972. if(type == 0) //flat
  973. return geo.getShort(index);
  974. else if(type == 1) //complex
  975. {
  976. cellX = getCell(x);
  977. cellY = getCell(y);
  978. index += ((cellX << 3) + cellY) << 1;
  979. short height = geo.getShort(index);
  980. NSWE = (short)(height&0x0F);
  981. height = (short)(height&0x0fff0);
  982. height = (short)(height >> 1); //height / 2
  983. if(checkNSWE(NSWE,x,y,tx,ty)) return height;
  984. else return Double.MIN_VALUE;
  985. }
  986. else //multilevel, type == 2
  987. {
  988. cellX = getCell(x);
  989. cellY = getCell(y);
  990. int offset = (cellX << 3) + cellY;
  991. while(offset > 0) // iterates (too many times?) to get to layer count
  992. {
  993. byte lc = geo.get(index);
  994. index += (lc << 1) + 1;
  995. offset--;
  996. }
  997. byte layers = geo.get(index);
  998. //_log.warning("layers"+layers);
  999. index++;
  1000. short height=-1;
  1001. if(layers <= 0 || layers > 125)
  1002. {
  1003. _log.warning("Broken geofile (case3), region: "+region+" - invalid layer count: "+layers+" at: "+x+" "+y);
  1004. return z;
  1005. }
  1006. short tempz = Short.MIN_VALUE;
  1007. while(layers > 0)
  1008. {
  1009. height = geo.getShort(index);
  1010. height = (short)(height&0x0fff0);
  1011. height = (short)(height >> 1); //height / 2
  1012. // searches the closest layer to current z coordinate
  1013. if ((z-tempz)*(z-tempz) > (z-height)*(z-height))
  1014. {
  1015. //layercurr = layers;
  1016. tempz = height;
  1017. NSWE = geo.getShort(index);
  1018. NSWE = (short)(NSWE&0x0F);
  1019. }
  1020. layers--;
  1021. index += 2;
  1022. }
  1023. if(checkNSWE(NSWE,x,y,tx,ty)) return tempz;
  1024. else return Double.MIN_VALUE;
  1025. }
  1026. }
  1027. /**
  1028. * @param x
  1029. * @param y
  1030. * @param z
  1031. * @param inc_x
  1032. * @param inc_y
  1033. * @param tz
  1034. * @return True if Char can see target
  1035. */
  1036. private static boolean nLOS(int x, int y, int z, int inc_x, int inc_y, double inc_z, int tz, boolean debug)
  1037. {
  1038. short region = getRegionOffset(x,y);
  1039. int blockX = getBlock(x);
  1040. int blockY = getBlock(y);
  1041. int cellX, cellY;
  1042. short NSWE = 0;
  1043. int index;
  1044. //Geodata without index - it is just empty so index can be calculated on the fly
  1045. if(_geodataIndex.get(region) == null) index = ((blockX << 8) + blockY)*3;
  1046. //Get Index for current block of current region geodata
  1047. else index = _geodataIndex.get(region).get(((blockX << 8))+(blockY));
  1048. //Buffer that Contains current Region GeoData
  1049. ByteBuffer geo = _geodata.get(region);
  1050. if(geo == null)
  1051. {
  1052. if(Config.DEBUG)
  1053. _log.warning("Geo Region - Region Offset: "+region+" dosnt exist!!");
  1054. return true;
  1055. }
  1056. //Read current block type: 0-flat,1-complex,2-multilevel
  1057. byte type = geo.get(index);
  1058. index++;
  1059. if(type == 0) //flat, movement and sight always possible
  1060. {
  1061. if(debug) _log.warning("flatheight:"+geo.getShort(index));
  1062. return true;
  1063. }
  1064. else if(type == 1) //complex
  1065. {
  1066. cellX = getCell(x);
  1067. cellY = getCell(y);
  1068. index += ((cellX << 3) + cellY) << 1;
  1069. short height = geo.getShort(index);
  1070. NSWE = (short)(height&0x0F);
  1071. height = (short)(height&0x0fff0);
  1072. height = (short)(height >> 1); //height / 2
  1073. if (!checkNSWE(NSWE,x,y,x+inc_x,y+inc_y))
  1074. {
  1075. if(debug) _log.warning("height:"+height+" z"+z);
  1076. if(z < nGetUpperHeight(x+inc_x,y+inc_y,height))
  1077. return false; // an obstacle high enough
  1078. return true;
  1079. }
  1080. else
  1081. return true;
  1082. }
  1083. else //multilevel, type == 2
  1084. {
  1085. cellX = getCell(x);
  1086. cellY = getCell(y);
  1087. int offset = (cellX << 3) + cellY;
  1088. while(offset > 0) // iterates (too many times?) to get to layer count
  1089. {
  1090. byte lc = geo.get(index);
  1091. index += (lc << 1) + 1;
  1092. offset--;
  1093. }
  1094. byte layers = geo.get(index);
  1095. index++;
  1096. short tempZ=-1;
  1097. if(layers <= 0 || layers > 125)
  1098. {
  1099. _log.warning("Broken geofile (case4), region: "+region+" - invalid layer count: "+layers+" at: "+x+" "+y);
  1100. return false;
  1101. }
  1102. short upperHeight = Short.MAX_VALUE; // big positive value
  1103. short lowerHeight = Short.MIN_VALUE; // big negative value
  1104. byte temp_layers = layers;
  1105. boolean highestlayer = false;
  1106. while(temp_layers > 0) // from higher to lower
  1107. {
  1108. // reads tempZ for current layer, result in world z coordinate
  1109. tempZ = geo.getShort(index);
  1110. tempZ = (short)(tempZ&0x0fff0);
  1111. tempZ = (short)(tempZ >> 1); //tempZ / 2
  1112. if (z > tempZ) {
  1113. lowerHeight = tempZ;
  1114. NSWE = geo.getShort(index);
  1115. NSWE = (short)(NSWE&0x0F);
  1116. break;
  1117. }
  1118. else {
  1119. highestlayer = false;
  1120. upperHeight = tempZ;
  1121. }
  1122. temp_layers--;
  1123. index += 2;
  1124. }
  1125. if(debug) _log.warning("z:"+z+" x: "+cellX+" y:"+cellY+" la "+layers+" lo:"+lowerHeight+" up:"+upperHeight);
  1126. // Check if LOS goes under a layer/floor
  1127. // clearly under layer but not too much under
  1128. // lowerheight here only for geodata bug checking, layers very close? maybe could be removed
  1129. if((z-upperHeight) < -10 && (z-upperHeight) > inc_z-10 && (z-lowerHeight) > 40) {
  1130. if (debug) _log.warning("false, incz"+inc_z);
  1131. return false;
  1132. }
  1133. // or there's a fence/wall ahead when we're not on highest layer
  1134. if(!highestlayer)
  1135. {
  1136. //a probable wall, there's movement block and layers above you
  1137. if(!checkNSWE(NSWE,x,y,x+inc_x,y+inc_y)) // cannot move
  1138. {
  1139. if(debug) _log.warning("block and next in x"+inc_x+" y"+inc_y+" is:"+nGetUpperHeight(x+inc_x,y+inc_y,lowerHeight));
  1140. // check one inc_x inc_y further, for the height there
  1141. if(z < nGetUpperHeight(x+inc_x,y+inc_y,lowerHeight))
  1142. return false; // a wall
  1143. return true; // we see over it, e.g. a fence
  1144. }
  1145. else return true;
  1146. }
  1147. if (!checkNSWE(NSWE,x,y,x+inc_x,y+inc_y))
  1148. {
  1149. // check one inc_x inc_y further, for the height there
  1150. if(z < nGetUpperHeight(x+inc_x,y+inc_y,lowerHeight))
  1151. return false; // we hit an obstacle high enough
  1152. return true;
  1153. }
  1154. else
  1155. return true;
  1156. }
  1157. }
  1158. /**
  1159. * @param x
  1160. * @param y
  1161. * @param z
  1162. * @return NSWE: 0-15
  1163. */
  1164. private short nGetNSWE(int x, int y, int z)
  1165. {
  1166. short region = getRegionOffset(x,y);
  1167. int blockX = getBlock(x);
  1168. int blockY = getBlock(y);
  1169. int cellX, cellY;
  1170. short NSWE = 0;
  1171. int index = 0;
  1172. //Geodata without index - it is just empty so index can be calculated on the fly
  1173. if(_geodataIndex.get(region) == null) index = ((blockX << 8) + blockY)*3;
  1174. //Get Index for current block of current region geodata
  1175. else index = _geodataIndex.get(region).get(((blockX << 8))+(blockY));
  1176. //Buffer that Contains current Region GeoData
  1177. ByteBuffer geo = _geodata.get(region);
  1178. if(geo == null)
  1179. {
  1180. if(Config.DEBUG)
  1181. _log.warning("Geo Region - Region Offset: "+region+" dosnt exist!!");
  1182. return 15;
  1183. }
  1184. //Read current block type: 0-flat,1-complex,2-multilevel
  1185. byte type = geo.get(index);
  1186. index++;
  1187. if(type == 0)//flat
  1188. return 15;
  1189. else if(type == 1)//complex
  1190. {
  1191. cellX = getCell(x);
  1192. cellY = getCell(y);
  1193. index += ((cellX << 3) + cellY) << 1;
  1194. short height = geo.getShort(index);
  1195. NSWE = (short)(height&0x0F);
  1196. }
  1197. else//multilevel
  1198. {
  1199. cellX = getCell(x);
  1200. cellY = getCell(y);
  1201. int offset = (cellX << 3) + cellY;
  1202. while(offset > 0)
  1203. {
  1204. byte lc = geo.get(index);
  1205. index += (lc << 1) + 1;
  1206. offset--;
  1207. }
  1208. byte layers = geo.get(index);
  1209. index++;
  1210. short height=-1;
  1211. if(layers <= 0 || layers > 125)
  1212. {
  1213. _log.warning("Broken geofile (case5), region: "+region+" - invalid layer count: "+layers+" at: "+x+" "+y);
  1214. return 15;
  1215. }
  1216. short tempz = Short.MIN_VALUE;
  1217. while(layers > 0)
  1218. {
  1219. height = geo.getShort(index);
  1220. height = (short)(height&0x0fff0);
  1221. height = (short)(height >> 1); //height / 2
  1222. if ((z-tempz)*(z-tempz) > (z-height)*(z-height))
  1223. {
  1224. tempz = height;
  1225. NSWE = geo.get(index);
  1226. NSWE = (short)(NSWE&0x0F);
  1227. }
  1228. layers--;
  1229. index += 2;
  1230. }
  1231. }
  1232. return NSWE;
  1233. }
  1234. /**
  1235. * @param NSWE
  1236. * @param x
  1237. * @param y
  1238. * @param tx
  1239. * @param ty
  1240. * @return True if NSWE dont block given direction
  1241. */
  1242. private static boolean checkNSWE(short NSWE, int x, int y, int tx, int ty)
  1243. {
  1244. //Check NSWE
  1245. if(NSWE == 15)
  1246. return true;
  1247. if(tx > x)//E
  1248. {
  1249. if ((NSWE & _e) == 0)
  1250. return false;
  1251. }
  1252. else if (tx < x)//W
  1253. {
  1254. if ((NSWE & _w) == 0)
  1255. return false;
  1256. }
  1257. if (ty > y)//S
  1258. {
  1259. if ((NSWE & _s) == 0)
  1260. return false;
  1261. }
  1262. else if (ty < y)//N
  1263. {
  1264. if ((NSWE & _n) == 0)
  1265. return false;
  1266. }
  1267. return true;
  1268. }
  1269. }