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.
 
 
 

475 line
11 KiB

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