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.
 
 
 

476 lines
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. };
  209. events.emit('onAfterGetLayerObjects', info);
  210. }
  211. if (layer.objects) {
  212. let len = data.length;
  213. for (let j = 0; j < len; j++) {
  214. let cell = data[j];
  215. builders.object(layerName, cell, j);
  216. }
  217. } else {
  218. for (let x = 0; x < w; x++) {
  219. for (let y = 0; y < h; y++) {
  220. let index = (y * oldW) + x;
  221. const info = {
  222. map: this.name,
  223. layer: layerName,
  224. cell: 0,
  225. x,
  226. y
  227. };
  228. if (x < oldW && y < oldH)
  229. info.cell = data[index];
  230. events.emit('onBeforeBuildLayerTile', info);
  231. builders.tile(info);
  232. }
  233. }
  234. }
  235. }
  236. },
  237. builders: {
  238. getCellInfo: function (cell) {
  239. let flipX = null;
  240. if ((cell ^ 0x80000000) > 0) {
  241. flipX = true;
  242. cell = cell ^ 0x80000000;
  243. }
  244. let firstGid = 0;
  245. let sheetName = null;
  246. for (let s = 0; s < mapFile.tilesets.length; s++) {
  247. let tileset = mapFile.tilesets[s];
  248. if (tileset.firstgid <= cell) {
  249. sheetName = tileset.name;
  250. firstGid = tileset.firstgid;
  251. }
  252. }
  253. cell = cell - firstGid + 1;
  254. return {
  255. sheetName: sheetName,
  256. cell: cell,
  257. flipX: flipX
  258. };
  259. },
  260. tile: function (info) {
  261. let { x, y, cell, layer: layerName } = info;
  262. if (cell === 0) {
  263. if (layerName === 'tiles')
  264. this.collisionMap[x][y] = 1;
  265. return;
  266. }
  267. let cellInfo = this.builders.getCellInfo(cell);
  268. if (!cellInfo.sheetName)
  269. console.log(x, y, cell);
  270. let sheetName = cellInfo.sheetName;
  271. cell = cellInfo.cell;
  272. if (sheetName === 'walls')
  273. cell += 224;
  274. else if (sheetName === 'objects')
  275. cell += 480;
  276. if ((layerName !== 'hiddenWalls') && (layerName !== 'hiddenTiles')) {
  277. let layer = this.layers;
  278. if (this.oldLayers[layerName])
  279. this.oldLayers[layerName][x][y] = cell;
  280. layer[x][y] = (layer[x][y] === null) ? cell : layer[x][y] + ',' + cell;
  281. } else if (layerName === 'hiddenWalls')
  282. this.hiddenWalls[x][y] = cell;
  283. else if (layerName === 'hiddenTiles')
  284. this.hiddenTiles[x][y] = cell;
  285. if (layerName.indexOf('walls') > -1)
  286. this.collisionMap[x][y] = 1;
  287. else if (sheetName.toLowerCase().indexOf('tiles') > -1) {
  288. //Check for water and water-like tiles
  289. if ([6, 7, 54, 55, 62, 63, 154, 189, 190, 192, 193, 194, 195, 196, 197].indexOf(cell) > -1)
  290. this.collisionMap[x][y] = 1;
  291. }
  292. },
  293. object: function (layerName, cell) {
  294. //Fixes for newer versions of tiled
  295. cell.properties = objectifyProperties(cell.properties);
  296. cell.polyline = cell.polyline || cell.polygon;
  297. let clientObj = (layerName === 'clientObjects');
  298. let cellInfo = this.builders.getCellInfo(cell.gid);
  299. let name = (cell.name || '');
  300. let objZoneName = name;
  301. if (name.indexOf('|') > -1) {
  302. let split = name.split('|');
  303. name = split[0];
  304. objZoneName = split[1];
  305. }
  306. let blueprint = {
  307. clientObj: clientObj,
  308. sheetName: cellInfo.sheetName,
  309. cell: cellInfo.cell - 1,
  310. x: cell.x / mapScale,
  311. y: (cell.y / mapScale) - 1,
  312. name: name,
  313. properties: cell.properties || {},
  314. layerName: layerName
  315. };
  316. if (objZoneName !== name)
  317. blueprint.objZoneName = objZoneName;
  318. if (this.zone) {
  319. if ((this.zone.objects) && (this.zone.objects[objZoneName.toLowerCase()]))
  320. extend(blueprint, this.zone.objects[objZoneName.toLowerCase()]);
  321. else if ((this.zone.objects) && (this.zone.mobs[objZoneName.toLowerCase()]))
  322. extend(blueprint, this.zone.mobs[objZoneName.toLowerCase()]);
  323. }
  324. if (blueprint.blocking)
  325. this.collisionMap[blueprint.x][blueprint.y] = 1;
  326. if ((blueprint.properties.cpnNotice) || (blueprint.properties.cpnLightPatch) || (layerName === 'rooms') || (layerName === 'hiddenRooms')) {
  327. blueprint.y++;
  328. blueprint.width = cell.width / mapScale;
  329. blueprint.height = cell.height / mapScale;
  330. } else if (cell.width === 24)
  331. blueprint.x++;
  332. if (cell.polyline)
  333. mapObjects.polyline(this.size, blueprint, cell, mapScale);
  334. if (layerName === 'rooms') {
  335. if (blueprint.properties.exit) {
  336. let room = this.rooms.find(function (r) {
  337. return (!(
  338. (blueprint.x + blueprint.width < r.x) ||
  339. (blueprint.y + blueprint.height < r.y) ||
  340. (blueprint.x >= r.x + r.width) ||
  341. (blueprint.y >= r.y + r.height)
  342. ));
  343. });
  344. room.exits.push(blueprint);
  345. } else if (blueprint.properties.resource)
  346. resourceSpawner.register(blueprint.properties.resource, blueprint);
  347. else {
  348. blueprint.exits = [];
  349. blueprint.objects = [];
  350. this.rooms.push(blueprint);
  351. }
  352. } else if (layerName === 'hiddenRooms') {
  353. blueprint.fog = (cell.properties || {}).fog;
  354. blueprint.discoverable = (cell.properties || {}).discoverable;
  355. this.hiddenRooms.push(blueprint);
  356. } else if (!clientObj) {
  357. if (!mapFile.properties.isRandom)
  358. spawners.register(blueprint, blueprint.spawnCd || mapFile.properties.spawnCd);
  359. else {
  360. let room = this.rooms.find(function (r) {
  361. return (!(
  362. (blueprint.x < r.x) ||
  363. (blueprint.y < r.y) ||
  364. (blueprint.x >= r.x + r.width) ||
  365. (blueprint.y >= r.y + r.height)
  366. ));
  367. });
  368. room.objects.push(blueprint);
  369. }
  370. } else {
  371. if ((cell.width) && (!cell.polyline)) {
  372. blueprint.width = cell.width / mapScale;
  373. blueprint.height = cell.height / mapScale;
  374. }
  375. let obj = objects.buildObjects([blueprint], true).getSimple(true);
  376. this.objBlueprints.push(obj);
  377. }
  378. }
  379. },
  380. getSpawnPos: function (obj) {
  381. let stats = obj.components.find(c => (c.type === 'stats'));
  382. let level = stats.values.level;
  383. let spawns = this.spawn.filter(s => (((s.maxLevel) && (s.maxLevel >= level)) || (!s.maxLevel)));
  384. return spawns[0];
  385. },
  386. //Find if any spawns can path to a position. This is important for when maps change and players
  387. // log in on tiles that aren't blocking but not able to reach anywhere useful
  388. canPathFromPos: function (pos) {
  389. return canPathFromPos(this, pos);
  390. }
  391. };