You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

440 lines
8.1 KiB

  1. define([
  2. 'js/misc/pathfinder'
  3. ], function (
  4. pathfinder
  5. ) {
  6. let sqrt = Math.sqrt.bind(Math);
  7. let ceil = Math.ceil.bind(Math);
  8. let random = Math.random.bind(Math);
  9. return {
  10. graph: null,
  11. collisionMap: null,
  12. cells: [],
  13. width: 0,
  14. height: 0,
  15. init: function (collisionMap) {
  16. this.collisionMap = collisionMap;
  17. this.width = collisionMap.length;
  18. this.height = collisionMap[0].length;
  19. this.cells = _.get2dArray(this.width, this.height, 'array');
  20. this.graph = new pathfinder.Graph(collisionMap, {
  21. diagonal: true
  22. });
  23. },
  24. addRegion: function (obj) {
  25. let lowX = obj.x;
  26. let lowY = obj.y;
  27. let highX = lowX + obj.width;
  28. let highY = lowY + obj.height;
  29. let cells = this.cells;
  30. for (let i = lowX; i <= highX; i++) {
  31. let row = cells[i];
  32. for (let j = lowY; j <= highY; j++)
  33. row[j].push(obj);
  34. }
  35. },
  36. addObject: function (obj, x, y, fromX, fromY) {
  37. let row = this.cells[x];
  38. if (!row)
  39. return;
  40. let cell = row[y];
  41. if (!cell)
  42. return;
  43. let cLen = cell.length;
  44. for (let i = 0; i < cLen; i++) {
  45. let c = cell[i];
  46. //If we have fromX and fromY, check if the target cell doesn't contain the same obj (like a notice area)
  47. if ((c.width) && (fromX)) {
  48. if ((fromX < c.x) || (fromY < c.y) || (fromX >= c.x + c.width) || (fromY >= c.y + c.height)) {
  49. c.collisionEnter(obj);
  50. obj.collisionEnter(c);
  51. }
  52. } else {
  53. c.collisionEnter(obj);
  54. obj.collisionEnter(c);
  55. }
  56. }
  57. cell.push(obj);
  58. return true;
  59. },
  60. removeObject: function (obj, x, y, toX, toY) {
  61. let row = this.cells[x];
  62. if (!row)
  63. return;
  64. let cell = row[y];
  65. if (!cell)
  66. return;
  67. let oId = obj.id;
  68. let cLen = cell.length;
  69. for (let i = 0; i < cLen; i++) {
  70. let c = cell[i];
  71. if (c.id != oId) {
  72. //If we have toX and toY, check if the target cell doesn't contain the same obj (like a notice area)
  73. if ((c.width) && (toX)) {
  74. if ((toX < c.x) || (toY < c.y) || (toX >= c.x + c.width) || (toY >= c.y + c.height)) {
  75. c.collisionExit(obj);
  76. obj.collisionExit(c);
  77. }
  78. } else {
  79. c.collisionExit(obj);
  80. obj.collisionExit(c);
  81. }
  82. } else {
  83. cell.splice(i, 1);
  84. i--;
  85. cLen--;
  86. }
  87. }
  88. },
  89. isValid: function (x, y) {
  90. let row = this.cells[x];
  91. if ((!row) || (row.length <= y) || (!this.graph.grid[x][y]))
  92. return false;
  93. return true;
  94. },
  95. getCell: function (x, y) {
  96. let row = this.cells[x];
  97. if (!row)
  98. return [];
  99. let cell = row[y];
  100. if (!cell)
  101. return [];
  102. return cell;
  103. },
  104. getArea: function (x1, y1, x2, y2, filter) {
  105. let width = this.width;
  106. let height = this.height;
  107. x1 = ~~x1;
  108. y1 = ~~y1;
  109. x2 = ~~x2;
  110. y2 = ~~y2;
  111. if (x1 < 0)
  112. x1 = 0;
  113. else if (x2 >= width)
  114. x2 = width - 1;
  115. if (y1 < 0)
  116. y1 = 0;
  117. else if (y2 >= height)
  118. y2 = height - 1;
  119. let cells = this.cells;
  120. let grid = this.graph.grid;
  121. let result = [];
  122. for (let i = x1; i <= x2; i++) {
  123. let row = cells[i];
  124. let gridRow = grid[i];
  125. for (let j = y1; j <= y2; j++) {
  126. if (!gridRow[j])
  127. continue;
  128. let cell = row[j];
  129. let cLen = cell.length;
  130. for (let k = 0; k < cLen; k++) {
  131. let c = cell[k];
  132. if (filter) {
  133. if (filter(c))
  134. result.push(c);
  135. } else
  136. result.push(c);
  137. }
  138. }
  139. }
  140. return result;
  141. },
  142. getOpenCellInArea: function (x1, y1, x2, y2) {
  143. let width = this.width;
  144. let height = this.height;
  145. x1 = ~~x1;
  146. y1 = ~~y1;
  147. x2 = ~~x2;
  148. y2 = ~~y2;
  149. if (x1 < 0)
  150. x1 = 0;
  151. else if (x2 >= width)
  152. x2 = width - 1;
  153. if (y1 < 0)
  154. y1 = 0;
  155. else if (y2 >= height)
  156. y2 = height - 1;
  157. let cells = this.cells;
  158. let grid = this.graph.grid;
  159. let result = [];
  160. for (let i = x1; i <= x2; i++) {
  161. let row = cells[i];
  162. let gridRow = grid[i];
  163. for (let j = y1; j <= y2; j++) {
  164. if (!gridRow[j])
  165. continue;
  166. let cell = row[j];
  167. if (cell.length == 0) {
  168. return {
  169. x: i,
  170. y: j
  171. };
  172. }
  173. }
  174. }
  175. return result;
  176. },
  177. getPath: function (from, to) {
  178. let graph = this.graph;
  179. let grid = graph.grid;
  180. if (!to) {
  181. to = {
  182. x: ~~(random() * grid.length),
  183. y: ~~(random() * grid[0].length)
  184. };
  185. }
  186. let fromX = ~~from.x;
  187. let fromY = ~~from.y;
  188. if ((!grid[fromX]) || (grid[fromX].length <= fromY) || (fromX < 0) || (fromY < 0))
  189. return [];
  190. let toX = ~~to.x;
  191. let toY = ~~to.y;
  192. if ((!grid[toX]) || (grid[toX].length <= toY) || (toX < 0) || (toY < 0))
  193. return [];
  194. let path = pathfinder.astar.search(graph, {
  195. x: fromX,
  196. y: fromY
  197. }, {
  198. x: toX,
  199. y: toY
  200. }, {
  201. closest: true
  202. });
  203. return path;
  204. },
  205. isTileBlocking: function (x, y, mob, obj) {
  206. if ((x < 0) || (y < 0) || (x >= this.width) | (y >= this.height))
  207. return true;
  208. x = ~~x;
  209. y = ~~y;
  210. let node = this.graph.grid[x][y];
  211. return ((!node) || (node.weight == 0));
  212. },
  213. isCellOpen: function (x, y) {
  214. if ((x < 0) || (y < 0) || (x >= this.width) | (y >= this.height))
  215. return true;
  216. return (this.cells[x][y].length == 0);
  217. },
  218. hasLos: function (fromX, fromY, toX, toY) {
  219. if ((fromX < 0) || (fromY < 0) || (fromX >= this.width) | (fromY >= this.height) || (toX < 0) || (toY < 0) || (toX >= this.width) | (toY >= this.height))
  220. return false;
  221. let graphGrid = this.graph.grid;
  222. if ((!graphGrid[fromX][fromY]) || (!graphGrid[toX][toY]))
  223. return false;
  224. let dx = toX - fromX;
  225. let dy = toY - fromY;
  226. let distance = sqrt((dx * dx) + (dy * dy));
  227. dx /= distance;
  228. dy /= distance;
  229. fromX += 0.5;
  230. fromY += 0.5;
  231. distance = ceil(distance);
  232. let x = 0;
  233. let y = 0;
  234. for (let i = 0; i < distance; i++) {
  235. fromX += dx;
  236. fromY += dy;
  237. x = ~~fromX;
  238. y = ~~fromY;
  239. if (!graphGrid[x][y])
  240. return false;
  241. else if ((x == toX) && (y == toY))
  242. return true;
  243. }
  244. return true;
  245. },
  246. getClosestPos: function (fromX, fromY, toX, toY, target) {
  247. let tried = {};
  248. let hasLos = this.hasLos.bind(this, toX, toY);
  249. let width = this.width;
  250. let height = this.height;
  251. let collisionMap = this.collisionMap;
  252. let cells = this.cells;
  253. let reverseX = (fromX > toX);
  254. let reverseY = (fromY > toY);
  255. for (let c = 1; c <= 10; c++) {
  256. let x1 = toX - c;
  257. let y1 = toY - c;
  258. let x2 = toX + c;
  259. let y2 = toY + c;
  260. var lowX, lowY, highX, highY, incX, incY;
  261. if (reverseX) {
  262. incX = -1;
  263. lowX = x2;
  264. highX = x1 - 1;
  265. } else {
  266. incX = 1;
  267. lowX = x1;
  268. highX = x2 + 1;
  269. }
  270. if (reverseY) {
  271. incY = -1;
  272. lowY = y2;
  273. highY = y1 - 1;
  274. } else {
  275. incY = 1;
  276. lowY = y1;
  277. highY = y2 + 1;
  278. }
  279. for (let i = lowX; i != highX; i += incX) {
  280. if ((i < 0) || (i >= width))
  281. continue;
  282. let row = collisionMap[i];
  283. let cellRow = cells[i];
  284. let t = tried[i];
  285. if (!t)
  286. t = tried[i] = {};
  287. for (let j = lowY; j != highY; j += incY) {
  288. if (t[j])
  289. continue;
  290. t[j] = 1;
  291. if (
  292. ((i == toX) && (j == toY)) ||
  293. ((j < 0) || (j >= height)) ||
  294. (row[j])
  295. )
  296. continue;
  297. let cell = cellRow[j];
  298. let cLen = cell.length;
  299. let blocking = false;
  300. for (let k = 0; k < cLen; k++) {
  301. let aggro = cell[k].aggro;
  302. if (aggro) {
  303. blocking = aggro.list.some(a => a.obj == target);
  304. if (blocking)
  305. break;
  306. }
  307. }
  308. if (blocking)
  309. continue;
  310. else if (!hasLos(i, j))
  311. continue;
  312. return {
  313. x: i,
  314. y: j
  315. };
  316. }
  317. }
  318. }
  319. },
  320. mobsCollide: function (x, y, obj) {
  321. if ((x < 0) || (y < 0) || (x >= this.width) | (y >= this.height))
  322. return true;
  323. let cell = this.cells[x][y];
  324. let cLen = cell.length;
  325. if (cLen == 1)
  326. return false;
  327. let found = false;
  328. for (let i = 0; i < cLen; i++) {
  329. let c = cell[i];
  330. if (c.aggro) {
  331. if ((!found) && (c == obj))
  332. found = true;
  333. else
  334. return true;
  335. }
  336. }
  337. return false;
  338. },
  339. setCollision: function (x, y, collides) {
  340. let node = this.graph.grid[x][y];
  341. if (!node) {
  342. let grid = this.graph.grid;
  343. node = grid[x][y] = new pathfinder.gridNode(x, y, collides ? 0 : 1);
  344. }
  345. node.weight = collides ? 0 : 1;
  346. }
  347. };
  348. });