GeoEngine.java 47 KB

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