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.
 
 
 

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