Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

467 строки
8.6 KiB

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