GeoEngine.java 50 KB

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