選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

map.js 13 KiB

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