Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.
 
 
 

649 rader
15 KiB

  1. define([
  2. 'js/system/events',
  3. 'js/system/client',
  4. 'html!ui/templates/inventory/template',
  5. 'css!ui/templates/inventory/styles',
  6. 'html!ui/templates/inventory/templateItem',
  7. 'html!ui/templates/inventory/templateTooltip',
  8. 'js/input'
  9. ], function (
  10. events,
  11. client,
  12. template,
  13. styles,
  14. tplItem,
  15. tplTooltip,
  16. input
  17. ) {
  18. var qualityColors = [{
  19. r: 252,
  20. g: 252,
  21. b: 252
  22. }, {
  23. r: 7,
  24. g: 170,
  25. b: 214
  26. }, {
  27. r: 255,
  28. g: 255,
  29. b: 0
  30. }, {
  31. r: 192,
  32. g: 0,
  33. b: 207
  34. }, {
  35. r: 255,
  36. g: 108,
  37. b: 4
  38. }];
  39. return {
  40. tpl: template,
  41. centered: true,
  42. items: [],
  43. shiftDown: false,
  44. ctrlDown: false,
  45. dragItem: null,
  46. dragEl: null,
  47. hoverCell: null,
  48. modal: true,
  49. oldSpellsZIndex: 0,
  50. postRender: function () {
  51. this.onEvent('onGetItems', this.onGetItems.bind(this));
  52. this.onEvent('onDestroyItems', this.onDestroyItems.bind(this));
  53. this.onEvent('onShowInventory', this.toggle.bind(this));
  54. this.onEvent('onKeyDown', this.onKeyDown.bind(this));
  55. this.onEvent('onKeyUp', this.onKeyUp.bind(this));
  56. this.find('.grid')
  57. .on('mousemove', this.onMouseMove.bind(this))
  58. .on('mouseleave', this.onMouseDown.bind(this, null, null, false));
  59. this.find('.split-box .amount')
  60. .on('mousewheel', this.onChangeStackAmount.bind(this))
  61. .on('input', this.onEnterStackAmount.bind(this));
  62. this.find('.split-box').on('click', this.splitStackEnd.bind(this, true));
  63. this.find('.split-box .btnSplit').on('click', this.splitStackEnd.bind(this, null));
  64. this.find('.split-box .btnLess').on('click', this.onChangeStackAmount.bind(this, null, -1));
  65. this.find('.split-box .btnMore').on('click', this.onChangeStackAmount.bind(this, null, 1));
  66. },
  67. build: function () {
  68. var container = this.el.find('.grid')
  69. .empty();
  70. var items = this.items
  71. .filter(function (item) {
  72. return !item.eq;
  73. });
  74. var iLen = Math.max(items.length, 50);
  75. var rendered = [];
  76. for (var i = 0; i < iLen; i++) {
  77. var item = items.find(function (item) {
  78. return ((item.pos != null) && (item.pos == i));
  79. });
  80. if (!item) {
  81. var itemEl = $(tplItem)
  82. .appendTo(container);
  83. itemEl
  84. .on('mouseup', this.onMouseDown.bind(this, null, null, false))
  85. .on('mousemove', this.onHover.bind(this, itemEl, item))
  86. .on('mouseleave', this.hideTooltip.bind(this, itemEl, item))
  87. .children()
  88. .remove();
  89. continue;
  90. } else {
  91. rendered.push(item);
  92. }
  93. var imgX = -item.sprite[0] * 64;
  94. var imgY = -item.sprite[1] * 64;
  95. var itemEl = $(tplItem)
  96. .appendTo(container);
  97. var spritesheet = item.spritesheet || '../../../images/items.png';
  98. if (!item.spritesheet) {
  99. if (item.material)
  100. spritesheet = '../../../images/materials.png';
  101. else if (item.quest)
  102. spritesheet = '../../../images/questItems.png';
  103. }
  104. itemEl
  105. .data('item', item)
  106. .on('click', this.onClick.bind(this, item))
  107. .on('mousedown', this.onMouseDown.bind(this, itemEl, item, true))
  108. .on('mouseup', this.onMouseDown.bind(this, null, null, false))
  109. .on('mousemove', this.onHover.bind(this, itemEl, item))
  110. .on('mouseleave', this.hideTooltip.bind(this, itemEl, item))
  111. .find('.icon')
  112. .css('background', 'url(' + spritesheet + ') ' + imgX + 'px ' + imgY + 'px')
  113. .on('contextmenu', this.showContext.bind(this, item));
  114. if (item.quantity > 1)
  115. itemEl.find('.quantity').html(item.quantity);
  116. else if (item.eq)
  117. itemEl.find('.quantity').html('EQ');
  118. else if (item.active)
  119. itemEl.find('.quantity').html('EQ');
  120. if (item.eq)
  121. itemEl.addClass('eq');
  122. else if (item.isNew) {
  123. itemEl.addClass('new');
  124. itemEl.find('.quantity').html('NEW');
  125. }
  126. }
  127. },
  128. onClick: function (item) {
  129. var msg = {
  130. item: item,
  131. success: true
  132. };
  133. events.emit('beforeInventoryClickItem', msg);
  134. if (!msg.success)
  135. return;
  136. if (!this.ctrlDown)
  137. return;
  138. client.request({
  139. cpn: 'social',
  140. method: 'chat',
  141. data: {
  142. message: '{' + item.name + '}',
  143. item: item
  144. }
  145. });
  146. },
  147. onMouseDown: function (el, item, down, e) {
  148. if (e.button != 0)
  149. return;
  150. if (down) {
  151. this.dragEl = el.clone()
  152. .appendTo(this.find('.grid'))
  153. .hide()
  154. .on('mouseup', this.onMouseDown.bind(this, null, null, false))
  155. .addClass('dragging');
  156. this.dragItem = el;
  157. events.emit('onHideItemTooltip', this.hoverItem);
  158. this.hoverItem = null;
  159. } else if (this.dragItem) {
  160. var method = 'moveItem';
  161. if ((this.hoverCell) && (this.hoverCell[0] != this.dragItem[0])) {
  162. var placeholder = $('<div></div>')
  163. .insertAfter(this.dragItem);
  164. this.dragItem.insertBefore(this.hoverCell);
  165. this.hoverCell.insertBefore(placeholder);
  166. placeholder.remove();
  167. var msgs = [{
  168. id: this.dragItem.data('item').id,
  169. pos: this.dragItem.index()
  170. }];
  171. this.items.find(function (i) {
  172. return (i.id == this.dragItem.data('item').id)
  173. }, this).pos = this.dragItem.index();
  174. var hoverCellItem = this.hoverCell.data('item');
  175. if (hoverCellItem) {
  176. if ((hoverCellItem.name != this.dragItem.data('item').name) || (!hoverCellItem.quantity)) {
  177. msgs.push({
  178. id: hoverCellItem.id,
  179. pos: this.hoverCell.index()
  180. });
  181. this.items.find(function (i) {
  182. return (i.id == hoverCellItem.id)
  183. }, this).pos = this.hoverCell.index();
  184. } else {
  185. method = 'combineStacks';
  186. msgs = {
  187. fromId: this.dragItem.data('item').id,
  188. toId: hoverCellItem.id,
  189. };
  190. }
  191. }
  192. client.request({
  193. cpn: 'player',
  194. method: 'performAction',
  195. data: {
  196. cpn: 'inventory',
  197. method: method,
  198. data: msgs
  199. }
  200. });
  201. this.build();
  202. }
  203. this.dragItem = null;
  204. this.dragEl.remove();
  205. this.dragEl = null;
  206. this.hoverCell = null;
  207. this.find('.hover').removeClass('hover');
  208. }
  209. },
  210. onMouseMove: function (e) {
  211. if (!this.dragEl)
  212. return;
  213. var offset = this.find('.grid').offset();
  214. this.dragEl.css({
  215. left: e.clientX - offset.left - 40,
  216. top: e.clientY - offset.top - 40,
  217. display: 'block'
  218. });
  219. },
  220. showContext: function (item, e) {
  221. var menuItems = {
  222. drop: {
  223. text: 'drop',
  224. callback: this.performItemAction.bind(this, item, 'dropItem')
  225. },
  226. destroy: {
  227. text: 'destroy',
  228. callback: this.performItemAction.bind(this, item, 'destroyItem')
  229. },
  230. salvage: {
  231. text: 'salvage',
  232. callback: this.performItemAction.bind(this, item, 'salvageItem')
  233. },
  234. stash: {
  235. text: 'stash',
  236. callback: this.performItemAction.bind(this, item, 'stashItem')
  237. },
  238. learn: {
  239. text: 'learn',
  240. callback: this.performItemAction.bind(this, item, 'learnAbility')
  241. },
  242. activate: {
  243. text: 'activate',
  244. callback: this.performItemAction.bind(this, item, 'activateMtx')
  245. },
  246. use: {
  247. text: 'use',
  248. callback: this.performItemAction.bind(this, item, 'useItem')
  249. },
  250. equip: {
  251. text: 'equip',
  252. callback: this.performItemAction.bind(this, item, 'equip')
  253. },
  254. augment: {
  255. text: 'craft',
  256. callback: this.openAugmentUi.bind(this, item)
  257. },
  258. mail: {
  259. text: 'mail',
  260. callback: this.openMailUi.bind(this, item)
  261. },
  262. split: {
  263. text: 'split stack',
  264. callback: this.splitStackStart.bind(this, item)
  265. },
  266. divider: '----------'
  267. };
  268. if (item.eq) {
  269. menuItems.learn.text = 'unlearn';
  270. menuItems.equip.text = 'unequip';
  271. }
  272. if (item.active)
  273. menuItems.activate.text = 'deactivate';
  274. var config = [];
  275. if (item.ability)
  276. config.push(menuItems.learn);
  277. else if (item.type == 'mtx')
  278. config.push(menuItems.activate);
  279. else if ((item.type == 'toy') || (item.type == 'consumable'))
  280. config.push(menuItems.use);
  281. else if (item.slot) {
  282. config.push(menuItems.equip);
  283. if (!item.eq)
  284. config.push(menuItems.divider);
  285. if (!item.eq) {
  286. config.push(menuItems.augment);
  287. config.push(menuItems.divider);
  288. }
  289. }
  290. if ((!item.eq) && (!item.active)) {
  291. if (!item.quest) {
  292. if ((window.player.stash.active) && (!item.noStash))
  293. config.push(menuItems.stash);
  294. if (!item.noDrop)
  295. config.push(menuItems.drop);
  296. if ((!item.material) && (!item.noSalvage))
  297. config.push(menuItems.salvage);
  298. }
  299. if (!item.noDestroy)
  300. config.push(menuItems.destroy);
  301. }
  302. if (item.quantity > 1)
  303. config.push(menuItems.split);
  304. if ((!item.noDrop) && (!item.quest))
  305. config.push(menuItems.mail);
  306. if (config.length > 0)
  307. events.emit('onContextMenu', config, e);
  308. e.preventDefault;
  309. return false;
  310. },
  311. splitStackStart: function (item) {
  312. var box = this.find('.split-box').show();
  313. box.data('item', item);
  314. box.find('.amount')
  315. .val('1')
  316. .focus();
  317. },
  318. splitStackEnd: function (cancel, e) {
  319. var box = this.find('.split-box');
  320. if ((cancel) || (!e) || (e.target != box.find('.btnSplit')[0])) {
  321. if ((cancel) && (!$(e.target).hasClass('button')))
  322. box.hide();
  323. return;
  324. }
  325. box.hide();
  326. client.request({
  327. cpn: 'player',
  328. method: 'performAction',
  329. data: {
  330. cpn: 'inventory',
  331. method: 'splitStack',
  332. data: {
  333. itemId: box.data('item').id,
  334. stackSize: ~~this.find('.split-box .amount').val()
  335. }
  336. }
  337. });
  338. },
  339. onChangeStackAmount: function (e, amount) {
  340. var item = this.find('.split-box').data('item');
  341. var delta = e ? ((e.originalEvent.deltaY > 0) ? -1 : 1) : amount;
  342. if (this.shiftDown)
  343. delta *= 10;
  344. var amount = this.find('.split-box .amount');
  345. amount.val(Math.max(1, Math.min(item.quantity - 1, ~~amount.val() + delta)));
  346. },
  347. onEnterStackAmount: function (e) {
  348. var el = this.find('.split-box .amount');
  349. var val = el.val();
  350. if (val != ~~val)
  351. el.val('');
  352. else if (val) {
  353. var item = this.find('.split-box').data('item');
  354. if (val < 0)
  355. val = '';
  356. else if (val > item.quantity - 1)
  357. val = item.quantity - 1;
  358. el.val(val);
  359. }
  360. },
  361. hideTooltip: function () {
  362. if (this.dragEl) {
  363. this.hoverCell = null;
  364. return;
  365. }
  366. events.emit('onHideItemTooltip', this.hoverItem);
  367. this.hoverItem = null;
  368. },
  369. onHover: function (el, item, e) {
  370. if (this.dragEl) {
  371. this.hoverCell = el;
  372. this.find('.hover').removeClass('hover');
  373. el.addClass('hover');
  374. return;
  375. }
  376. if (item)
  377. this.hoverItem = item;
  378. else
  379. item = this.hoverItem;
  380. if (!item)
  381. return;
  382. var ttPos = null;
  383. if (el) {
  384. if (el.hasClass('new')) {
  385. el.removeClass('new');
  386. el.find('.quantity').html((item.quantity > 1) ? item.quantity : '');
  387. delete item.isNew;
  388. }
  389. var elOffset = el.offset();
  390. ttPos = {
  391. x: ~~(e.clientX + 32),
  392. y: ~~(e.clientY)
  393. };
  394. }
  395. var compare = null;
  396. if (item.slot) {
  397. compare = this.items.find(function (i) {
  398. return ((i.eq) && (i.slot == item.slot));
  399. });
  400. // check special cases for mismatched weapon/offhand scenarios (only valid when comparing)
  401. if ((!compare) && (this.shiftDown)) {
  402. var equippedTwoHanded = this.items.find(function (i) {
  403. return ((i.eq) && (i.slot == 'twoHanded'));
  404. });
  405. var equippedOneHanded = this.items.find(function (i) {
  406. return ((i.eq) && (i.slot == 'oneHanded'));
  407. });
  408. var equippedOffhand = this.items.find(function (i) {
  409. return ((i.eq) && (i.slot == 'offHand'));
  410. });
  411. if (item.slot == 'twoHanded') {
  412. if (!equippedOneHanded) {
  413. compare = equippedOffhand;
  414. } else if (!equippedOffhand) {
  415. compare = equippedOneHanded;
  416. } else {
  417. // compare against oneHanded and offHand combined by creating a virtual item that is the sum of the two
  418. compare = $.extend(true, {}, equippedOneHanded);
  419. compare.refItem = equippedOneHanded;
  420. for (var s in equippedOffhand.stats) {
  421. if (!compare.stats[s])
  422. compare.stats[s] = 0;
  423. compare.stats[s] += equippedOffhand.stats[s]
  424. }
  425. }
  426. }
  427. if (item.slot == 'oneHanded') {
  428. compare = equippedTwoHanded;
  429. }
  430. // this case is kind of ugly, but we don't want to go in when comparing an offHand to (oneHanded + empty offHand) - that should just use the normal compare which is offHand to empty
  431. if ((item.slot == 'offHand') && (equippedTwoHanded)) {
  432. // since we're comparing an offhand to an equipped Twohander, we need to clone the 'spell' values over (setting damage to zero) so that we can properly display how much damage
  433. // the player would lose by switching to the offhand (which would remove the twoHander)
  434. // keep a reference to the original item for use in onHideToolTip
  435. var spellClone = $.extend(true, {}, equippedTwoHanded.spell);
  436. spellClone.name = '';
  437. spellClone.values['damage'] = 0;
  438. var clone = $.extend(true, {}, item, {
  439. spell: spellClone
  440. });
  441. clone.refItem = item;
  442. item = clone;
  443. compare = equippedTwoHanded;
  444. }
  445. }
  446. }
  447. events.emit('onShowItemTooltip', item, ttPos, compare, false, this.shiftDown);
  448. },
  449. onGetItems: function (items, rerender) {
  450. this.items = items;
  451. if ((this.shown) && (rerender))
  452. this.build();
  453. },
  454. onDestroyItems: function (itemIds) {
  455. itemIds.forEach(function (id) {
  456. var item = this.items.find(i => i.id == id);
  457. if (item == this.hoverItem)
  458. this.hideTooltip();
  459. this.items.spliceWhere(i => i.id == id);
  460. }, this);
  461. if (this.shown)
  462. this.build();
  463. },
  464. toggle: function (show) {
  465. this.shown = !this.el.is(':visible');
  466. if (this.shown) {
  467. this.find('.split-box').hide();
  468. this.show();
  469. this.build();
  470. } else {
  471. this.hide();
  472. events.emit('onHideInventory');
  473. events.emit('onHideContextMenu');
  474. }
  475. this.hideTooltip();
  476. },
  477. beforeDestroy: function () {
  478. this.el.parent().css('background-color', 'transparent');
  479. this.el.parent().removeClass('blocking');
  480. },
  481. beforeHide: function () {
  482. if (this.oldSpellsZIndex) {
  483. $('.uiSpells').css('z-index', this.oldSpellsZIndex);
  484. this.oldSpellsZIndex = null;
  485. }
  486. },
  487. performItemAction: function (item, action) {
  488. var stats = window.player.stats.values;
  489. var playerLevel = stats.originalLevel || stats.level;
  490. if (!item)
  491. return;
  492. else if ((action == 'equip') && ((item.material) || (item.quest) || (item.type == 'mtx') || (!window.player.inventory.canEquipItem(item))))
  493. return;
  494. else if ((action == 'learnAbility') && (!window.player.inventory.canEquipItem(item)))
  495. return;
  496. else if ((action == 'activateMtx') && (item.type != 'mtx'))
  497. return;
  498. var cpn = 'inventory';
  499. if (action == 'equip')
  500. cpn = 'equipment';
  501. if (action == 'useItem')
  502. this.hide();
  503. client.request({
  504. cpn: 'player',
  505. method: 'performAction',
  506. data: {
  507. cpn: cpn,
  508. method: action,
  509. data: item.id
  510. }
  511. });
  512. },
  513. openAugmentUi: function (item) {
  514. events.emit('onSetSmithItem', {
  515. item: item
  516. });
  517. },
  518. openMailUi: function (item) {
  519. events.emit('onSetMailItem', {
  520. item: item
  521. });
  522. },
  523. onKeyDown: function (key) {
  524. if (key == 'i')
  525. this.toggle();
  526. else if (key == 'shift') {
  527. this.shiftDown = true;
  528. if (this.hoverItem)
  529. this.onHover();
  530. } else if (key == 'ctrl')
  531. this.ctrlDown = true;
  532. },
  533. onKeyUp: function (key) {
  534. if (key == 'shift') {
  535. this.shiftDown = false;
  536. if (this.hoverItem)
  537. this.onHover();
  538. } else if (key == 'ctrl')
  539. this.ctrlDown = false;
  540. }
  541. };
  542. });