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.
 
 
 

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