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.
 
 
 

481 rivejä
11 KiB

  1. let objects = require('../objects/objects');
  2. let physics = require('./physics');
  3. let spawners = require('./spawners');
  4. let globalZone = require('../config/zoneBase');
  5. let randomMap = require('./randomMap');
  6. let events = require('../misc/events');
  7. const mapObjects = require('./map/mapObjects');
  8. const canPathFromPos = require('./map/canPathFromPos');
  9. let mapFile = null;
  10. let mapScale = null;
  11. let padding = null;
  12. const objectifyProperties = oldProperties => {
  13. if (!oldProperties || !oldProperties.push)
  14. return oldProperties || {};
  15. let newProperties = {};
  16. oldProperties.forEach(p => {
  17. newProperties[p.name] = p.value;
  18. });
  19. return newProperties;
  20. };
  21. module.exports = {
  22. name: null,
  23. path: null,
  24. layers: [],
  25. mapFile: null,
  26. //The size of the base map, before mods are applied
  27. originalSize: {
  28. w: 0,
  29. h: 0
  30. },
  31. //The size of the map after mods are applied
  32. size: {
  33. w: 0,
  34. h: 0
  35. },
  36. custom: null,
  37. collisionMap: null,
  38. clientMap: null,
  39. oldLayers: {
  40. tiles: null,
  41. walls: null,
  42. doodads: null
  43. },
  44. objBlueprints: [],
  45. spawn: {
  46. x: 0,
  47. y: 0
  48. },
  49. rooms: [],
  50. hiddenRooms: [],
  51. hiddenWalls: null,
  52. hiddenTiles: null,
  53. zone: null,
  54. init: function (args) {
  55. this.name = args.name;
  56. this.path = args.path;
  57. try {
  58. this.zone = require('../' + this.path + '/' + this.name + '/zone');
  59. } catch (e) {
  60. this.zone = globalZone;
  61. }
  62. events.emit('onAfterGetZone', this.name, this.zone);
  63. let chats = null;
  64. try {
  65. chats = require('../' + this.path + '/' + this.name + '/chats');
  66. } catch (e) {}
  67. if (chats)
  68. this.zone.chats = chats;
  69. let dialogues = null;
  70. try {
  71. dialogues = require('../' + this.path + '/' + this.name + '/dialogues');
  72. } catch (e) {}
  73. events.emit('onBeforeGetDialogue', this.name, dialogues);
  74. if (dialogues)
  75. this.zone.dialogues = dialogues;
  76. this.zone = extend({}, globalZone, this.zone);
  77. mapFile = require('../' + this.path + '/' + this.name + '/map');
  78. this.mapFile = mapFile;
  79. //Fix for newer versions of Tiled
  80. this.mapFile.properties = objectifyProperties(this.mapFile.properties);
  81. mapScale = mapFile.tilesets[0].tileheight;
  82. this.custom = mapFile.properties.custom;
  83. if (mapFile.properties.spawn) {
  84. this.spawn = JSON.parse(mapFile.properties.spawn);
  85. if (!this.spawn.push)
  86. this.spawn = [this.spawn];
  87. }
  88. },
  89. create: function () {
  90. this.getMapFile();
  91. this.clientMap = {
  92. zoneId: -1,
  93. map: this.layers,
  94. collisionMap: this.collisionMap,
  95. clientObjects: this.objBlueprints,
  96. padding: padding,
  97. hiddenRooms: this.hiddenRooms
  98. };
  99. },
  100. getMapFile: function () {
  101. this.build();
  102. randomMap = extend({}, randomMap);
  103. this.oldMap = this.layers;
  104. randomMap.templates = extend([], this.rooms);
  105. randomMap.generateMappings(this);
  106. for (let i = 0; i < this.size.w; i++) {
  107. let row = this.layers[i];
  108. for (let j = 0; j < this.size.h; j++) {
  109. let cell = row[j];
  110. if (!cell)
  111. continue;
  112. cell = cell.split(',');
  113. let cLen = cell.length;
  114. let newCell = '';
  115. for (let k = 0; k < cLen; k++) {
  116. let c = cell[k];
  117. let newC = randomMap.randomizeTile(c);
  118. newCell += newC;
  119. //Wall?
  120. if ((c >= 160) && (c <= 352) && (newC === 0))
  121. this.collisionMap[i][j] = 0;
  122. if (k < cLen - 1)
  123. newCell += ',';
  124. }
  125. let fakeContents = [];
  126. const hiddenWall = this.hiddenWalls[i][j];
  127. const hiddenTile = this.hiddenTiles[i][j];
  128. if (hiddenTile)
  129. fakeContents.push(-randomMap.randomizeTile(hiddenTile));
  130. if (hiddenWall)
  131. fakeContents.push(-randomMap.randomizeTile(hiddenWall));
  132. if (fakeContents.length)
  133. newCell += ',' + fakeContents.join(',');
  134. row[j] = newCell;
  135. }
  136. }
  137. //Fix for newer versions of Tiled
  138. randomMap.templates
  139. .forEach(r => {
  140. r.properties = objectifyProperties(r.properties);
  141. });
  142. randomMap.templates
  143. .filter(r => r.properties.mapping)
  144. .forEach(function (m) {
  145. let x = m.x;
  146. let y = m.y;
  147. let w = m.width;
  148. let h = m.height;
  149. for (let i = x; i < x + w; i++) {
  150. let row = this.layers[i];
  151. for (let j = y; j < y + h; j++)
  152. row[j] = '';
  153. }
  154. }, this);
  155. physics.init(this.collisionMap);
  156. padding = mapFile.properties.padding;
  157. mapFile = null;
  158. _.log('(M ' + this.name + '): Ready');
  159. },
  160. build: function () {
  161. events.emit('onBeforeBuildMap', this.name, this.zone);
  162. const mapSize = {
  163. w: mapFile.width,
  164. h: mapFile.height
  165. };
  166. this.originalSize = {
  167. w: mapFile.width,
  168. h: mapFile.height
  169. };
  170. events.emit('onBeforeGetMapSize', this.name, mapSize);
  171. this.size.w = mapSize.w;
  172. this.size.h = mapSize.h;
  173. const { w: oldW, h: oldH } = this.originalSize;
  174. const { w, h } = this.size;
  175. this.layers = _.get2dArray(w, h, null);
  176. this.hiddenWalls = _.get2dArray(w, h, null);
  177. this.hiddenTiles = _.get2dArray(w, h, null);
  178. this.oldLayers.tiles = _.get2dArray(w, h, 0);
  179. this.oldLayers.walls = _.get2dArray(w, h, 0);
  180. this.oldLayers.objects = _.get2dArray(w, h, 0);
  181. let builders = {
  182. tile: this.builders.tile.bind(this),
  183. object: this.builders.object.bind(this)
  184. };
  185. this.collisionMap = _.get2dArray(w, h);
  186. //Rooms need to be ahead of exits
  187. mapFile.layers.rooms = (mapFile.layers.rooms || [])
  188. .sort(function (a, b) {
  189. if ((a.exit) && (!b.exit))
  190. return 1;
  191. return 0;
  192. });
  193. for (let i = 0; i < mapFile.layers.length; i++) {
  194. let layer = mapFile.layers[i];
  195. let layerName = layer.name;
  196. if (!layer.visible)
  197. continue;
  198. let data = layer.data || layer.objects;
  199. let firstItem = data[0];
  200. if (firstItem && firstItem.has('width')) {
  201. let info = {
  202. map: this.name,
  203. layer: layerName,
  204. objects: data,
  205. mapScale
  206. };
  207. events.emit('onAfterGetLayerObjects', info);
  208. }
  209. if (layer.objects) {
  210. let len = data.length;
  211. for (let j = 0; j < len; j++) {
  212. let cell = data[j];
  213. builders.object(layerName, cell, j);
  214. }
  215. } else {
  216. for (let x = 0; x < w; x++) {
  217. for (let y = 0; y < h; y++) {
  218. let index = (y * oldW) + x;
  219. const info = {
  220. map: this.name,
  221. layer: layerName,
  222. cell: 0,
  223. x,
  224. y
  225. };
  226. if (x < oldW && y < oldH)
  227. info.cell = data[index];
  228. events.emit('onBeforeBuildLayerTile', info);
  229. builders.tile(info);
  230. }
  231. }
  232. }
  233. }
  234. },
  235. builders: {
  236. getCellInfo: function (cell) {
  237. let flipX = null;
  238. if ((cell ^ 0x80000000) > 0) {
  239. flipX = true;
  240. cell = cell ^ 0x80000000;
  241. }
  242. let firstGid = 0;
  243. let sheetName = null;
  244. for (let s = 0; s < mapFile.tilesets.length; s++) {
  245. let tileset = mapFile.tilesets[s];
  246. if (tileset.firstgid <= cell) {
  247. sheetName = tileset.name;
  248. firstGid = tileset.firstgid;
  249. }
  250. }
  251. cell = cell - firstGid + 1;
  252. return {
  253. sheetName: sheetName,
  254. cell: cell,
  255. flipX: flipX
  256. };
  257. },
  258. tile: function (info) {
  259. let { x, y, cell, layer: layerName } = info;
  260. if (cell === 0) {
  261. if (layerName === 'tiles')
  262. this.collisionMap[x][y] = 1;
  263. return;
  264. }
  265. let cellInfo = this.builders.getCellInfo(cell);
  266. let sheetName = cellInfo.sheetName;
  267. cell = cellInfo.cell;
  268. if (sheetName === 'walls')
  269. cell += 224;
  270. else if (sheetName === 'objects')
  271. cell += 480;
  272. if ((layerName !== 'hiddenWalls') && (layerName !== 'hiddenTiles')) {
  273. let layer = this.layers;
  274. if (this.oldLayers[layerName])
  275. this.oldLayers[layerName][x][y] = cell;
  276. layer[x][y] = (layer[x][y] === null) ? cell : layer[x][y] + ',' + cell;
  277. } else if (layerName === 'hiddenWalls')
  278. this.hiddenWalls[x][y] = cell;
  279. else if (layerName === 'hiddenTiles')
  280. this.hiddenTiles[x][y] = cell;
  281. if (layerName.indexOf('walls') > -1)
  282. this.collisionMap[x][y] = 1;
  283. else if (layerName === 'tiles' && sheetName === 'tiles') {
  284. //Check for water and water-like tiles
  285. if ([6, 7, 54, 55, 62, 63, 154, 189, 190, 192, 193, 194, 195, 196, 197].indexOf(cell) > -1)
  286. this.collisionMap[x][y] = 1;
  287. }
  288. },
  289. object: function (layerName, cell) {
  290. const buildObjectMsg = {
  291. layerName,
  292. mapScale,
  293. obj: cell,
  294. zoneConfig: this.zone,
  295. ignore: false
  296. };
  297. events.emit('onBeforeBuildMapObject', buildObjectMsg);
  298. if (buildObjectMsg.built)
  299. return;
  300. //Fixes for newer versions of tiled
  301. cell.properties = objectifyProperties(cell.properties);
  302. cell.polyline = cell.polyline || cell.polygon;
  303. let clientObj = (layerName === 'clientObjects');
  304. let cellInfo = this.builders.getCellInfo(cell.gid);
  305. let name = (cell.name || '');
  306. let objZoneName = name;
  307. if (name.indexOf('|') > -1) {
  308. let split = name.split('|');
  309. name = split[0];
  310. objZoneName = split[1];
  311. }
  312. let blueprint = {
  313. clientObj: clientObj,
  314. sheetName: cell.has('sheetName') ? cell.sheetName : cellInfo.sheetName,
  315. cell: cell.has('cell') ? cell.cell : cellInfo.cell - 1,
  316. x: cell.x / mapScale,
  317. y: (cell.y / mapScale) - 1,
  318. name: name,
  319. properties: cell.properties || {},
  320. layerName: layerName
  321. };
  322. if (objZoneName !== name)
  323. blueprint.objZoneName = objZoneName;
  324. if (this.zone) {
  325. if ((this.zone.objects) && (this.zone.objects[objZoneName.toLowerCase()]))
  326. extend(blueprint, this.zone.objects[objZoneName.toLowerCase()]);
  327. else if ((this.zone.objects) && (this.zone.mobs[objZoneName.toLowerCase()]))
  328. extend(blueprint, this.zone.mobs[objZoneName.toLowerCase()]);
  329. }
  330. if (blueprint.blocking)
  331. this.collisionMap[blueprint.x][blueprint.y] = 1;
  332. if ((blueprint.properties.cpnNotice) || (blueprint.properties.cpnLightPatch) || (layerName === 'rooms') || (layerName === 'hiddenRooms')) {
  333. blueprint.y++;
  334. blueprint.width = cell.width / mapScale;
  335. blueprint.height = cell.height / mapScale;
  336. } else if (cell.width === 24)
  337. blueprint.x++;
  338. if (cell.polyline)
  339. mapObjects.polyline(this.size, blueprint, cell, mapScale);
  340. if (layerName === 'rooms') {
  341. if (blueprint.properties.exit) {
  342. let room = this.rooms.find(function (r) {
  343. return (!(
  344. (blueprint.x + blueprint.width < r.x) ||
  345. (blueprint.y + blueprint.height < r.y) ||
  346. (blueprint.x >= r.x + r.width) ||
  347. (blueprint.y >= r.y + r.height)
  348. ));
  349. });
  350. room.exits.push(blueprint);
  351. } else {
  352. blueprint.exits = [];
  353. blueprint.objects = [];
  354. this.rooms.push(blueprint);
  355. }
  356. } else if (layerName === 'hiddenRooms') {
  357. blueprint.fog = (cell.properties || {}).fog;
  358. blueprint.discoverable = (cell.properties || {}).discoverable;
  359. this.hiddenRooms.push(blueprint);
  360. } else if (!clientObj) {
  361. if (!mapFile.properties.isRandom)
  362. spawners.register(blueprint, blueprint.spawnCd || mapFile.properties.spawnCd);
  363. else {
  364. let room = this.rooms.find(function (r) {
  365. return (!(
  366. (blueprint.x < r.x) ||
  367. (blueprint.y < r.y) ||
  368. (blueprint.x >= r.x + r.width) ||
  369. (blueprint.y >= r.y + r.height)
  370. ));
  371. });
  372. room.objects.push(blueprint);
  373. }
  374. } else {
  375. if ((cell.width) && (!cell.polyline)) {
  376. blueprint.width = cell.width / mapScale;
  377. blueprint.height = cell.height / mapScale;
  378. }
  379. let obj = objects.buildObjects([blueprint], true).getSimple(true);
  380. this.objBlueprints.push(obj);
  381. }
  382. }
  383. },
  384. getSpawnPos: function (obj) {
  385. let stats = obj.components.find(c => (c.type === 'stats'));
  386. let level = stats.values.level;
  387. let spawns = this.spawn.filter(s => (((s.maxLevel) && (s.maxLevel >= level)) || (!s.maxLevel)));
  388. return spawns[0];
  389. },
  390. //Find if any spawns can path to a position. This is important for when maps change and players
  391. // log in on tiles that aren't blocking but not able to reach anywhere useful
  392. canPathFromPos: function (pos) {
  393. return canPathFromPos(this, pos);
  394. }
  395. };