GeoEngine.java 45 KB

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