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.
 
 
 

507 regels
9.1 KiB

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