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.
 
 
 

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