您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 

807 行
18 KiB

  1. define([
  2. 'js/resources',
  3. 'js/system/events',
  4. 'js/misc/physics',
  5. 'js/rendering/effects',
  6. 'js/rendering/tileOpacity',
  7. 'js/rendering/particles',
  8. 'js/rendering/shaders/outline',
  9. 'js/rendering/spritePool',
  10. 'picture'
  11. ], function (
  12. resources,
  13. events,
  14. physics,
  15. effects,
  16. tileOpacity,
  17. particles,
  18. shaderOutline,
  19. spritePool,
  20. picture
  21. ) {
  22. let pixi = PIXI;
  23. let mRandom = Math.random.bind(Math);
  24. return {
  25. stage: null,
  26. layers: {
  27. objects: null,
  28. mobs: null,
  29. characters: null,
  30. attacks: null,
  31. effects: null,
  32. particles: null,
  33. lightPatches: null,
  34. lightBeams: null,
  35. tileSprites: null,
  36. hiders: null
  37. },
  38. titleScreen: false,
  39. width: 0,
  40. height: 0,
  41. showTilesW: 0,
  42. showTilesH: 0,
  43. pos: {
  44. x: 0,
  45. y: 0
  46. },
  47. moveTo: null,
  48. moveSpeed: 0,
  49. moveSpeedMax: 1.50,
  50. moveSpeedInc: 0.5,
  51. lastUpdatePos: {
  52. x: 0,
  53. y: 0
  54. },
  55. zoneId: null,
  56. textures: {},
  57. textureCache: {},
  58. sprites: [],
  59. lastTick: null,
  60. hiddenRooms: null,
  61. init: function () {
  62. PIXI.GC_MODES.DEFAULT = PIXI.GC_MODES.AUTO;
  63. PIXI.SCALE_MODES.DEFAULT = PIXI.SCALE_MODES.NEAREST;
  64. events.on('onGetMap', this.onGetMap.bind(this));
  65. events.on('onToggleFullscreen', this.toggleScreen.bind(this));
  66. this.width = $('body').width();
  67. this.height = $('body').height();
  68. this.showTilesW = Math.ceil((this.width / scale) / 2) + 3;
  69. this.showTilesH = Math.ceil((this.height / scale) / 2) + 3;
  70. this.renderer = pixi.autoDetectRenderer(this.width, this.height, {
  71. backgroundColor: '0x2d2136'
  72. });
  73. window.onresize = this.onResize.bind(this);
  74. $(this.renderer.view)
  75. .appendTo('.canvas-container');
  76. this.stage = new pixi.Container();
  77. let layers = this.layers;
  78. Object.keys(layers).forEach(function (l) {
  79. layers[l] = new pixi.Container();
  80. layers[l].layer = (l === 'tileSprites') ? 'tiles' : l;
  81. this.stage.addChild(layers[l]);
  82. }, this);
  83. let spriteNames = ['tiles', 'mobs', 'bosses', 'animBigObjects', 'bigObjects', 'objects', 'characters', 'attacks', 'auras', 'walls', 'ui', 'animChar', 'animMob', 'animBoss', 'white', 'ray'];
  84. resources.spriteNames.forEach(function (s) {
  85. if (s.indexOf('.png') > -1)
  86. spriteNames.push(s);
  87. });
  88. spriteNames.forEach(function (t) {
  89. this.textures[t] = new pixi.BaseTexture(resources.sprites[t].image);
  90. this.textures[t].scaleMode = pixi.SCALE_MODES.NEAREST;
  91. }, this);
  92. particles.init({
  93. r: this,
  94. renderer: this.renderer,
  95. stage: this.layers.particles
  96. });
  97. this.buildSpritesTexture();
  98. },
  99. buildSpritesTexture: function () {
  100. let container = new pixi.Container();
  101. let totalHeight = 0;
  102. ['tiles', 'walls', 'objects'].forEach(function (t) {
  103. let texture = this.textures[t];
  104. let tile = new pixi.Sprite(new pixi.Texture(texture));
  105. tile.width = texture.width;
  106. tile.height = texture.height;
  107. tile.x = 0;
  108. tile.y = totalHeight;
  109. container.addChild(tile);
  110. totalHeight += tile.height;
  111. }, this);
  112. let renderTexture = pixi.RenderTexture.create(this.textures.tiles.width, totalHeight);
  113. this.renderer.render(container, renderTexture);
  114. this.textures.sprites = renderTexture;
  115. this.textures.scaleMult = pixi.SCALE_MODES.NEAREST;
  116. },
  117. toggleScreen: function () {
  118. let isFullscreen = (window.innerHeight === screen.height);
  119. if (isFullscreen) {
  120. let doc = document;
  121. (doc.cancelFullscreen || doc.msCancelFullscreen || doc.mozCancelFullscreen || doc.webkitCancelFullScreen).call(doc);
  122. return 'Windowed';
  123. }
  124. let el = $('body')[0];
  125. (el.requestFullscreen || el.msRequestFullscreen || el.mozRequestFullscreen || el.webkitRequestFullscreen).call(el);
  126. return 'Fullscreen';
  127. },
  128. buildTitleScreen: function () {
  129. this.titleScreen = true;
  130. this.setPosition({
  131. x: 0,
  132. y: 0
  133. }, true);
  134. let w = Math.ceil(this.width / scale) + 1;
  135. let h = Math.ceil(this.height / scale) + 1;
  136. let container = this.layers.tileSprites;
  137. for (let i = 0; i < w; i++) {
  138. let ii = i / 10;
  139. for (let j = 0; j < h; j++) {
  140. let roll = Math.sin(((j * 0.2) % 5) + Math.cos(ii % 8));
  141. let tile = 5;
  142. if (roll < -0.2)
  143. tile = 3;
  144. else if (roll < 0.2)
  145. tile = 4;
  146. else if (roll < 0.5)
  147. tile = 53;
  148. let alpha = mRandom();
  149. if ([5, 53].indexOf(tile) > -1)
  150. alpha *= 2;
  151. alpha = Math.min(Math.max(0.15, alpha), 0.65);
  152. if (mRandom() < 0.35) {
  153. tile = {
  154. 5: 6,
  155. 3: 0,
  156. 4: 1,
  157. 53: 54
  158. }[tile];
  159. }
  160. let sprite = new pixi.Sprite(this.getTexture('sprites', tile));
  161. sprite.alpha = alpha;
  162. sprite.position.x = i * scale;
  163. sprite.position.y = j * scale;
  164. sprite.width = scale;
  165. sprite.height = scale;
  166. if (mRandom() < 0.5) {
  167. sprite.position.x += scale;
  168. sprite.scale.x = -scaleMult;
  169. }
  170. container.addChild(sprite);
  171. }
  172. }
  173. },
  174. onResize: function () {
  175. if (isMobile)
  176. return;
  177. let zoom = window.devicePixelRatio;
  178. this.width = $('body').width() * zoom;
  179. this.height = $('body').height() * zoom;
  180. this.showTilesW = Math.ceil((this.width / scale) / 2) + 3;
  181. this.showTilesH = Math.ceil((this.height / scale) / 2) + 3;
  182. this.renderer.resize(this.width, this.height);
  183. if (window.player) {
  184. this.setPosition({
  185. x: (window.player.x - (this.width / (scale * 2))) * scale,
  186. y: (window.player.y - (this.height / (scale * 2))) * scale
  187. }, true);
  188. }
  189. if (this.titleScreen) {
  190. this.clean();
  191. this.buildTitleScreen();
  192. }
  193. events.emit('onResize');
  194. },
  195. getTexture: function (baseTex, cell, size) {
  196. size = size || 8;
  197. let textureName = baseTex + '_' + cell;
  198. let textureCache = this.textureCache;
  199. let cached = textureCache[textureName];
  200. if (!cached) {
  201. let y = ~~(cell / 8);
  202. let x = cell - (y * 8);
  203. cached = new pixi.Texture(this.textures[baseTex], new pixi.Rectangle(x * size, y * size, size, size));
  204. textureCache[textureName] = cached;
  205. }
  206. return cached;
  207. },
  208. clean: function () {
  209. this.stage.removeChild(this.layers.hiders);
  210. this.layers.hiders = new pixi.Container();
  211. this.layers.hiders.layer = 'hiders';
  212. this.stage.addChild(this.layers.hiders);
  213. let container = this.layers.tileSprites;
  214. this.stage.removeChild(container);
  215. this.layers.tileSprites = container = new pixi.Container();
  216. container.layer = 'tiles';
  217. this.stage.addChild(container);
  218. this.stage.children.sort(function (a, b) {
  219. if (a.layer === 'hiders')
  220. return 1;
  221. else if (b.layer === 'hiders')
  222. return -1;
  223. else if (a.layer === 'tiles')
  224. return -1;
  225. else if (b.layer === 'tiles')
  226. return 1;
  227. return 0;
  228. });
  229. },
  230. buildTile: function (c, i, j) {
  231. let alpha = tileOpacity.map(c);
  232. let canFlip = tileOpacity.canFlip(c);
  233. let tile = new pixi.Sprite(this.getTexture('sprites', c));
  234. tile.alpha = alpha;
  235. tile.position.x = i * scale;
  236. tile.position.y = j * scale;
  237. tile.width = scale;
  238. tile.height = scale;
  239. if (canFlip && mRandom() < 0.5) {
  240. tile.position.x += scale;
  241. tile.scale.x = -scaleMult;
  242. }
  243. return tile;
  244. },
  245. onGetMap: function (msg) {
  246. this.titleScreen = false;
  247. physics.init(msg.collisionMap);
  248. let map = this.map = msg.map;
  249. let w = this.w = map.length;
  250. let h = this.h = map[0].length;
  251. for (let i = 0; i < w; i++) {
  252. let row = map[i];
  253. for (let j = 0; j < h; j++) {
  254. if (!row[j].split)
  255. row[j] += '';
  256. row[j] = row[j].split(',');
  257. }
  258. }
  259. this.clean();
  260. spritePool.clean();
  261. this.stage.filters = [new PIXI.filters.VoidFilter()];
  262. this.stage.filterArea = new PIXI.Rectangle(0, 0, w * scale, h * scale);
  263. this.hiddenRooms = msg.hiddenRooms;
  264. this.sprites = _.get2dArray(w, h, 'array');
  265. this.stage.children.sort(function (a, b) {
  266. if (a.layer === 'tiles')
  267. return -1;
  268. else if (b.layer === 'tiles')
  269. return 1;
  270. return 0;
  271. }, this);
  272. if (this.zoneId !== null)
  273. events.emit('onRezone', this.zoneId);
  274. this.zoneId = msg.zoneId;
  275. msg.clientObjects.forEach(function (c) {
  276. c.zoneId = this.zoneId;
  277. events.emit('onGetObject', c);
  278. }, this);
  279. },
  280. setPosition: function (pos, instant) {
  281. pos.x += 16;
  282. pos.y += 16;
  283. let player = window.player;
  284. if (player) {
  285. let px = player.x;
  286. let py = player.y;
  287. let hiddenRooms = this.hiddenRooms;
  288. let hLen = hiddenRooms.length;
  289. for (let i = 0; i < hLen; i++) {
  290. let h = hiddenRooms[i];
  291. if (!h.discoverable)
  292. continue;
  293. if (
  294. px < h.x ||
  295. px >= h.x + h.width ||
  296. py < h.y ||
  297. py >= h.y + h.height ||
  298. !physics.isInPolygon(px, py, h.area)
  299. )
  300. continue;
  301. h.discovered = true;
  302. }
  303. }
  304. if (instant) {
  305. this.moveTo = null;
  306. this.pos = pos;
  307. this.stage.x = -~~this.pos.x;
  308. this.stage.y = -~~this.pos.y;
  309. } else
  310. this.moveTo = pos;
  311. this.updateSprites();
  312. },
  313. isVisible: function (x, y) {
  314. let stage = this.stage;
  315. let sx = -stage.x;
  316. let sy = -stage.y;
  317. let sw = this.width;
  318. let sh = this.height;
  319. return (!(x < sx || y < sy || x >= sx + sw || y >= sy + sh));
  320. },
  321. isHidden: function (x, y) {
  322. let hiddenRooms = this.hiddenRooms;
  323. let hLen = hiddenRooms.length;
  324. if (!hLen)
  325. return false;
  326. let player = window.player;
  327. let px = player.x;
  328. let py = player.y;
  329. let hidden = false;
  330. for (let i = 0; i < hLen; i++) {
  331. let h = hiddenRooms[i];
  332. let outsideHider = (
  333. x < h.x ||
  334. x >= h.x + h.width ||
  335. y < h.y ||
  336. y >= h.y + h.height
  337. );
  338. if (outsideHider)
  339. continue;
  340. let inHider = physics.isInPolygon(x, y, h.area);
  341. if (!inHider)
  342. continue;
  343. if (h.discovered)
  344. return false;
  345. outsideHider = (
  346. px < h.x ||
  347. px >= h.x + h.width ||
  348. py < h.y ||
  349. py >= h.y + h.height
  350. );
  351. if (outsideHider) {
  352. hidden = true;
  353. continue;
  354. }
  355. inHider = physics.isInPolygon(px, py, h.area);
  356. if (inHider)
  357. return false;
  358. hidden = true;
  359. }
  360. return hidden;
  361. },
  362. updateSprites: function () {
  363. if (this.titleScreen)
  364. return;
  365. let player = window.player;
  366. if (!player)
  367. return;
  368. let w = this.w;
  369. let h = this.h;
  370. let x = ~~((-this.stage.x / scale) + (this.width / (scale * 2)));
  371. let y = ~~((-this.stage.y / scale) + (this.height / (scale * 2)));
  372. this.lastUpdatePos.x = this.stage.x;
  373. this.lastUpdatePos.y = this.stage.y;
  374. let sprites = this.sprites;
  375. let map = this.map;
  376. let container = this.layers.tileSprites;
  377. let sw = this.showTilesW;
  378. let sh = this.showTilesH;
  379. let lowX = Math.max(0, x - sw + 1);
  380. let lowY = Math.max(0, y - sh + 2);
  381. let highX = Math.min(w, x + sw - 2);
  382. let highY = Math.min(h, y + sh - 2);
  383. let addedSprite = false;
  384. let checkHidden = this.isHidden.bind(this);
  385. let newVisible = [];
  386. let newHidden = [];
  387. for (let i = lowX; i < highX; i++) {
  388. let mapRow = map[i];
  389. let spriteRow = sprites[i];
  390. for (let j = lowY; j < highY; j++) {
  391. let cell = mapRow[j];
  392. if (!cell)
  393. continue;
  394. let cLen = cell.length;
  395. if (!cLen)
  396. return;
  397. let rendered = spriteRow[j];
  398. let isHidden = checkHidden(i, j);
  399. if (rendered.length > 0) {
  400. if (!isHidden)
  401. continue;
  402. else {
  403. newHidden.push({
  404. x: i,
  405. y: j
  406. });
  407. let rLen = rendered.length;
  408. for (let k = 0; k < rLen; k++) {
  409. let sprite = rendered[k];
  410. sprite.visible = false;
  411. spritePool.store(sprite);
  412. }
  413. spriteRow[j] = [];
  414. continue;
  415. }
  416. } else if (isHidden)
  417. continue;
  418. newVisible.push({
  419. x: i,
  420. y: j
  421. });
  422. for (let k = 0; k < cLen; k++) {
  423. let c = cell[k];
  424. if (c === '0' || c === '')
  425. continue;
  426. c--;
  427. let flipped = '';
  428. if (tileOpacity.canFlip(c)) {
  429. if (mRandom() < 0.5)
  430. flipped = 'flip';
  431. }
  432. let tile = spritePool.getSprite(flipped + c);
  433. if (!tile) {
  434. tile = this.buildTile(c, i, j);
  435. container.addChild(tile);
  436. tile.type = c;
  437. tile.sheetNum = tileOpacity.getSheetNum(c);
  438. addedSprite = true;
  439. } else {
  440. tile.position.x = i * scale;
  441. tile.position.y = j * scale;
  442. if (flipped !== '')
  443. tile.position.x += scale;
  444. tile.visible = true;
  445. }
  446. tile.z = k;
  447. rendered.push(tile);
  448. }
  449. }
  450. }
  451. lowX = Math.max(0, lowX - 10);
  452. lowY = Math.max(0, lowY - 10);
  453. highX = Math.min(w - 1, highX + 10);
  454. highY = Math.min(h - 1, highY + 10);
  455. for (let i = lowX; i < highX; i++) {
  456. let spriteRow = sprites[i];
  457. let outside = ((i >= x - sw) && (i < x + sw));
  458. for (let j = lowY; j < highY; j++) {
  459. if ((outside) && (j >= y - sh) && (j < y + sh))
  460. continue;
  461. let list = spriteRow[j];
  462. let lLen = list.length;
  463. for (let k = 0; k < lLen; k++) {
  464. let sprite = list[k];
  465. sprite.visible = false;
  466. spritePool.store(sprite);
  467. }
  468. spriteRow[j] = [];
  469. }
  470. }
  471. events.emit('onTilesVisible', newVisible, true);
  472. events.emit('onTilesVisible', newHidden, false);
  473. if (addedSprite)
  474. container.children.sort((a, b) => a.z - b.z);
  475. },
  476. update: function () {
  477. let time = +new Date();
  478. if (this.moveTo) {
  479. let deltaX = this.moveTo.x - this.pos.x;
  480. let deltaY = this.moveTo.y - this.pos.y;
  481. if (deltaX !== 0 || deltaY !== 0) {
  482. let moveSpeed = this.moveSpeed;
  483. let distance = Math.max(Math.abs(deltaX), Math.abs(deltaY));
  484. let moveSpeedMax = this.moveSpeedMax;
  485. if (distance > 100)
  486. moveSpeedMax *= 1.75;
  487. if (this.moveSpeed < moveSpeedMax)
  488. this.moveSpeed += this.moveSpeedInc;
  489. let elapsed = time - this.lastTick;
  490. moveSpeed *= (elapsed / 16.67);
  491. if (moveSpeed > distance)
  492. moveSpeed = distance;
  493. deltaX = (deltaX / distance) * moveSpeed;
  494. deltaY = (deltaY / distance) * moveSpeed;
  495. this.pos.x = this.pos.x + deltaX;
  496. this.pos.y = this.pos.y + deltaY;
  497. } else {
  498. this.moveSpeed = 0;
  499. this.moveTo = null;
  500. }
  501. let stage = this.stage;
  502. stage.x = -~~this.pos.x;
  503. stage.y = -~~this.pos.y;
  504. let halfScale = scale / 2;
  505. if (Math.abs(stage.x - this.lastUpdatePos.x) > halfScale || Math.abs(stage.y - this.lastUpdatePos.y) > halfScale)
  506. this.updateSprites();
  507. events.emit('onSceneMove');
  508. }
  509. this.lastTick = time;
  510. },
  511. buildContainer: function (obj) {
  512. let container = new pixi.Container();
  513. this.layers[obj.layerName || obj.sheetName].addChild(container);
  514. return container;
  515. },
  516. buildRectangle: function (obj) {
  517. let graphics = new pixi.Graphics();
  518. let alpha = obj.alpha;
  519. if (obj.has('alpha'))
  520. graphics.alpha = alpha;
  521. let fillAlpha = obj.fillAlpha;
  522. if (obj.has('fillAlpha'))
  523. fillAlpha = 1;
  524. graphics.beginFill(obj.color || '0x48edff', fillAlpha);
  525. if (obj.strokeColor)
  526. graphics.lineStyle(scaleMult, obj.strokeColor);
  527. graphics.drawRect(0, 0, obj.w, obj.h);
  528. graphics.endFill();
  529. (obj.parent || this.layers[obj.layerName || obj.sheetName]).addChild(graphics);
  530. graphics.position.x = obj.x;
  531. graphics.position.y = obj.y;
  532. return graphics;
  533. },
  534. moveRectangle: function (obj) {
  535. obj.sprite.position.x = obj.x;
  536. obj.sprite.position.y = obj.y;
  537. obj.sprite.width = obj.w;
  538. obj.sprite.height = obj.h;
  539. },
  540. buildObject: function (obj) {
  541. let w = 8;
  542. let h = 8;
  543. if (obj.w) {
  544. w = obj.w / scaleMult;
  545. h = obj.h / scaleMult;
  546. }
  547. let bigSheets = ['bosses', 'bigObjects', 'animBigObjects'];
  548. if ((bigSheets.indexOf(obj.sheetName) > -1) || (obj.sheetName.indexOf('bosses') > -1)) {
  549. obj.layerName = 'mobs';
  550. w = 24;
  551. h = 24;
  552. obj.w = w * scaleMult;
  553. obj.h = h * scaleMult;
  554. }
  555. let sprite = new pixi.Sprite(this.getTexture(obj.sheetName, obj.cell, w));
  556. sprite.x = obj.x * scale;
  557. sprite.y = obj.y * scale;
  558. sprite.width = obj.w || scale;
  559. sprite.height = obj.h || scale;
  560. sprite.visible = obj.has('visible') ? obj.visible : true;
  561. if ((bigSheets.indexOf(obj.sheetName) > -1) || (obj.sheetName.indexOf('bosses') > -1)) {
  562. sprite.x -= scale;
  563. sprite.y -= (scale * 2);
  564. }
  565. if (obj.flipX) {
  566. sprite.scale.x *= -1;
  567. if (bigSheets.indexOf(obj.sheetName) > -1)
  568. sprite.x += (scale * 2);
  569. else
  570. sprite.x += scale;
  571. }
  572. (obj.parent || this.layers[obj.layerName || obj.sheetName] || this.layers.objects).addChild(sprite);
  573. return sprite;
  574. },
  575. addFilter: function (sprite) {
  576. let thickness = (sprite.width > scale) ? 8 : 16;
  577. let filter = new shaderOutline(this.renderer.width, this.renderer.height, thickness, '0xffffff');
  578. if (!sprite.filters)
  579. sprite.filters = [filter];
  580. else
  581. sprite.filters.push();
  582. return filter;
  583. },
  584. removeFilter: function (sprite, filter) {
  585. if (sprite.filters)
  586. sprite.filters = null;
  587. },
  588. buildText: function (obj) {
  589. let textSprite = new pixi.Text(obj.text, {
  590. fontFamily: 'bitty',
  591. fontSize: (obj.fontSize || 14),
  592. fill: obj.color || 0xF2F5F5,
  593. stroke: 0x2d2136,
  594. strokeThickness: 4,
  595. align: 'center'
  596. });
  597. textSprite.x = obj.x - (textSprite.width / 2);
  598. textSprite.y = obj.y;
  599. let parentSprite = obj.parent || this.layers[obj.layerName];
  600. parentSprite.addChild(textSprite);
  601. return textSprite;
  602. },
  603. buildEmitter: function (config) {
  604. return particles.buildEmitter(config);
  605. },
  606. destroyEmitter: function (emitter) {
  607. particles.destroyEmitter(emitter);
  608. },
  609. setSprite: function (obj) {
  610. obj.sprite.texture = this.getTexture(obj.sheetName, obj.cell, obj.sprite.width / scaleMult);
  611. },
  612. reorder: function (sprite) {
  613. this.layers.mobs.children.sort((a, b) => b.y - a.y);
  614. },
  615. destroyObject: function (obj) {
  616. if (obj.sprite.parent)
  617. obj.sprite.parent.removeChild(obj.sprite);
  618. },
  619. render: function () {
  620. if (!this.stage)
  621. return;
  622. effects.render();
  623. particles.update();
  624. this.renderer.render(this.stage);
  625. }
  626. };
  627. });