25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

499 lines
12 KiB

  1. define([
  2. 'js/system/events',
  3. 'js/system/client',
  4. 'html!ui/templates/equipment/template',
  5. 'css!ui/templates/equipment/styles',
  6. 'js/input',
  7. 'ui/shared/renderItem'
  8. ], function (
  9. events,
  10. client,
  11. template,
  12. styles,
  13. input,
  14. renderItem
  15. ) {
  16. return {
  17. tpl: template,
  18. centered: true,
  19. modal: true,
  20. hasClose: true,
  21. stats: null,
  22. equipment: null,
  23. hoverItem: null,
  24. hoverEl: null,
  25. hoverCompare: null,
  26. isInspecting: false,
  27. postRender: function () {
  28. this.onEvent('onGetStats', this.onGetStats.bind(this));
  29. this.onEvent('onGetItems', this.onGetItems.bind(this));
  30. this.onEvent('onInspectTarget', this.onInspectTarget.bind(this));
  31. this.onEvent('onShowEquipment', this.toggle.bind(this));
  32. this.find('.tab').on('click', this.onTabClick.bind(this));
  33. this.onEvent('onKeyDown', this.onKeyDown.bind(this));
  34. this.onEvent('onKeyUp', this.onKeyUp.bind(this));
  35. },
  36. beforeHide: function () {
  37. this.isInspecting = false;
  38. delete this.result;
  39. },
  40. toggle: function (show) {
  41. this.shown = !this.el.is(':visible');
  42. this.isInspecting = false;
  43. delete this.result;
  44. this.find('.itemList').hide();
  45. if (this.shown) {
  46. this.show();
  47. this.onGetStats();
  48. this.onGetItems();
  49. } else
  50. this.hide();
  51. this.onHoverItem(null, null, null);
  52. },
  53. onKeyDown: function (key) {
  54. if (key === 'j')
  55. this.toggle();
  56. else if (key === 'shift' && this.hoverItem)
  57. this.onHoverItem(this.hoverEl, this.hoverItem, this.hoverCompare);
  58. },
  59. onKeyUp: function (key) {
  60. if (key === 'shift' && this.hoverItem)
  61. this.onHoverItem(this.hoverEl, this.hoverItem, null);
  62. },
  63. onTabClick: function (e) {
  64. this.find('.tab.selected').removeClass('selected');
  65. $(e.target).addClass('selected');
  66. let stats = this.isInspecting ? this.result.stats : this.stats;
  67. this.onGetStats(stats);
  68. },
  69. onGetItems: function (items) {
  70. items = items || this.items;
  71. if (!this.isInspecting)
  72. this.items = items;
  73. if (!this.shown)
  74. return;
  75. this.find('.slot').addClass('empty');
  76. this.find('[slot]')
  77. .removeData('item')
  78. .addClass('empty show-default-icon')
  79. .find('.info')
  80. .html('')
  81. .parent()
  82. .find('.icon')
  83. .off()
  84. .css('background-image', '')
  85. .css('background-position', '')
  86. .on('click', this.buildSlot.bind(this));
  87. this.find('[slot]').toArray().forEach(el => {
  88. el = $(el);
  89. let slot = el.attr('slot');
  90. let newItems = window.player.inventory.items.some(i => {
  91. if (slot.indexOf('finger') === 0)
  92. slot = 'finger';
  93. else if (slot === 'oneHanded')
  94. return (['oneHanded', 'twoHanded'].includes(i.slot) && i.isNew);
  95. return (i.slot === slot && i.isNew);
  96. });
  97. if (newItems)
  98. el.find('.info').html('new');
  99. });
  100. items
  101. .filter(item => item.has('quickSlot') || (item.eq && (item.slot || item.has('runeSlot'))))
  102. .forEach(item => {
  103. let slot = item.slot;
  104. if (item.has('runeSlot')) {
  105. let runeSlot = item.runeSlot;
  106. slot = 'rune-' + runeSlot;
  107. } else if (item.has('quickSlot'))
  108. slot = 'quick-' + item.quickSlot;
  109. slot = item.equipSlot || slot;
  110. const elSlot = this.find('[slot="' + slot + '"]')
  111. .removeClass('empty show-default-icon');
  112. const itemEl = renderItem(null, item, elSlot);
  113. itemEl
  114. .data('item', item)
  115. .removeClass('empty show-default-icon')
  116. .find('.icon')
  117. .off()
  118. .on('contextmenu', this.showContext.bind(this, item))
  119. .on('mousedown', this.buildSlot.bind(this, elSlot))
  120. .on('mousemove', this.onHoverItem.bind(this, elSlot, item, null))
  121. .on('mouseleave', this.onHoverItem.bind(this, null, null));
  122. });
  123. },
  124. openAugmentUi: function (item) {
  125. events.emit('onSetSmithItem', {
  126. item: item
  127. });
  128. },
  129. showContext: function (item, e) {
  130. let menuItems = {
  131. unequip: {
  132. text: 'unequip',
  133. callback: this.unequipItem.bind(this, item)
  134. },
  135. augment: {
  136. text: 'craft',
  137. callback: this.openAugmentUi.bind(this, item)
  138. }
  139. };
  140. let config = [];
  141. config.push(menuItems.unequip);
  142. if (item.slot)
  143. config.push(menuItems.augment);
  144. events.emit('onContextMenu', config, e);
  145. e.preventDefault();
  146. return false;
  147. },
  148. unequipItem: function (item) {
  149. const isQuickslot = item.has('quickSlot');
  150. const method = isQuickslot ? 'setQuickSlot' : 'unequip';
  151. const data = isQuickslot ? { slot: item.quickSlot } : item.id;
  152. client.request({
  153. cpn: 'player',
  154. method: 'performAction',
  155. data: {
  156. cpn: 'equipment',
  157. method,
  158. data
  159. }
  160. });
  161. },
  162. onInspectTarget: function (result) {
  163. this.isInspecting = true;
  164. this.show();
  165. this.result = result;
  166. this.onGetStats(result.stats);
  167. this.onGetItems(result.equipment);
  168. },
  169. buildSlot: function (el, e) {
  170. if (e && e.button !== 0)
  171. return;
  172. if (this.isInspecting)
  173. return;
  174. if (el.target)
  175. el = $(el.target).parent();
  176. let slot = el.attr('slot');
  177. let isRune = (slot.indexOf('rune') === 0);
  178. const isConsumable = (slot.indexOf('quick') === 0);
  179. let container = this.find('.itemList')
  180. .empty()
  181. .show();
  182. let hoverCompare = this.hoverCompare = el.data('item');
  183. let items = this.items
  184. .filter(item => {
  185. if (isRune)
  186. return (!item.slot && item.spell && !item.eq);
  187. else if (isConsumable)
  188. return (item.type === 'consumable' && !item.has('quickSlot'));
  189. let checkSlot = (slot.indexOf('finger') === 0) ? 'finger' : slot;
  190. if (slot === 'oneHanded')
  191. return (!item.eq && (item.slot === 'oneHanded' || item.slot === 'twoHanded'));
  192. return (item.slot === checkSlot && !item.eq);
  193. });
  194. if (isConsumable)
  195. items = items.filter((item, i) => items.findIndex(f => f.name === item.name) === i);
  196. items.splice(0, 0, {
  197. name: 'None',
  198. slot: hoverCompare ? hoverCompare.slot : null,
  199. id: (hoverCompare && !isConsumable) ? hoverCompare.id : null,
  200. type: isConsumable ? 'consumable' : null,
  201. empty: true
  202. });
  203. if (hoverCompare)
  204. items.splice(1, 0, hoverCompare);
  205. items
  206. .forEach(function (item, i) {
  207. let sprite = item.sprite || [7, 0];
  208. let spriteSheet = item.empty ? '../../../images/uiIcons.png' : item.spritesheet || '../../../images/items.png';
  209. if (i > 0 && item.type === 'consumable')
  210. spriteSheet = '../../../images/consumables.png';
  211. let imgX = -sprite[0] * 64;
  212. let imgY = -sprite[1] * 64;
  213. let itemEl = $('<div class="slot"><div class="icon"></div></div>')
  214. .appendTo(container);
  215. itemEl
  216. .find('.icon')
  217. .css('background', 'url("' + spriteSheet + '") ' + imgX + 'px ' + imgY + 'px')
  218. .on('mousedown', this.equipItem.bind(this, item, slot))
  219. .on('mousemove', this.onHoverItem.bind(this, itemEl, item, null))
  220. .on('mouseleave', this.onHoverItem.bind(this, null, null));
  221. if (item === hoverCompare)
  222. itemEl.find('.icon').addClass('eq');
  223. else if (item.isNew)
  224. el.find('.icon').addClass('new');
  225. }, this);
  226. if (!items.length)
  227. container.hide();
  228. if (e) {
  229. e.preventDefault();
  230. return false;
  231. }
  232. },
  233. equipItem: function (item, slot, e) {
  234. let isNew = window.player.inventory.items.some(f => (f.equipSlot === slot && f.isNew));
  235. if (!isNew)
  236. this.find('[slot="' + slot + '"] .info').html('');
  237. if (item === this.hoverCompare) {
  238. this.find('.itemList').hide();
  239. return;
  240. }
  241. let cpn = 'equipment';
  242. let method = 'equip';
  243. let data = item.id;
  244. if (item.empty)
  245. method = 'unequip';
  246. if (item.type === 'consumable') {
  247. cpn = 'equipment';
  248. method = 'setQuickSlot';
  249. data = {
  250. itemId: item.id,
  251. slot: ~~slot.replace('quick-', '')
  252. };
  253. } else if (!item.slot) {
  254. cpn = 'inventory';
  255. method = 'learnAbility';
  256. data = {
  257. itemId: item.id,
  258. slot: ~~slot.replace('rune-', '')
  259. };
  260. if (item.empty) {
  261. if (!this.hoverCompare) {
  262. this.find('.itemList').hide();
  263. return;
  264. }
  265. method = 'unlearnAbility';
  266. data.itemId = this.hoverCompare.id;
  267. }
  268. } else if (item.slot === 'finger') {
  269. data = {
  270. itemId: item.id,
  271. slot: slot
  272. };
  273. }
  274. client.request({
  275. cpn: 'player',
  276. method: 'performAction',
  277. data: {
  278. cpn: cpn,
  279. method: method,
  280. data: data
  281. }
  282. });
  283. this.find('.itemList').hide();
  284. e.preventDefault();
  285. return false;
  286. },
  287. onHoverItem: function (el, item, compare, e) {
  288. if (el) {
  289. this.hoverItem = item;
  290. this.hoverEl = el;
  291. if ((item.isNew) && (!item.eq)) {
  292. delete item.isNew;
  293. el.find('.icon').removeClass('new');
  294. }
  295. let ttPos = null;
  296. if (e) {
  297. ttPos = {
  298. x: ~~(e.clientX + 32),
  299. y: ~~(e.clientY)
  300. };
  301. }
  302. events.emit('onShowItemTooltip', item, ttPos, this.hoverCompare);
  303. } else {
  304. events.emit('onHideItemTooltip', this.hoverItem);
  305. this.hoverItem = null;
  306. }
  307. },
  308. onGetStats: function (stats ) {
  309. if (stats && !this.isInspecting)
  310. this.stats = stats;
  311. stats = stats || this.stats;
  312. if (!this.shown)
  313. return;
  314. let container = this.el.find('.stats');
  315. container
  316. .children('*:not(.tabs)')
  317. .remove();
  318. let xpRemaining = (stats.xpMax - stats.xp).toString().replace(/\B(?=(\d{3})+(?!\d))/g, ',');
  319. let newStats = {
  320. basic: {
  321. level: stats.level,
  322. 'next level': xpRemaining + 'xp',
  323. gap1: '',
  324. gold: window.player.trade.gold,
  325. gap2: '',
  326. hp: ~~stats.hp + '/' + ~~stats.hpMax,
  327. mana: ~~stats.mana + '/' + ~~stats.manaMax,
  328. 'hp regen': stats.regenHp,
  329. 'mana regen': ~~stats.regenMana + '%',
  330. gap3: '',
  331. str: stats.str,
  332. int: stats.int,
  333. dex: stats.dex,
  334. vit: stats.vit
  335. },
  336. offense: {
  337. 'global crit chance': (~~(stats.critChance * 10) / 10) + '%',
  338. 'global crit multiplier': (~~(stats.critMultiplier * 10) / 10) + '%',
  339. 'attack crit chance': (~~((stats.critChance + stats.attackCritChance) * 10) / 10) + '%',
  340. 'attack crit multiplier': (~~((stats.critMultiplier + stats.attackCritMultiplier) * 10) / 10) + '%',
  341. 'spell crit chance': (~~((stats.critChance + stats.spellCritChance) * 10) / 10) + '%',
  342. 'spell crit multiplier': (~~((stats.critMultiplier + stats.spellCritMultiplier) * 10) / 10) + '%',
  343. gap1: '',
  344. 'arcane increase': stats.elementArcanePercent + '%',
  345. 'fire increase': stats.elementFirePercent + '%',
  346. 'frost increase': stats.elementFrostPercent + '%',
  347. 'holy increase': stats.elementHolyPercent + '%',
  348. 'poison increase': stats.elementPoisonPercent + '%',
  349. 'physical increase': stats.physicalPercent + '%',
  350. gap2: '',
  351. 'spell increase': stats.spellPercent + '%',
  352. gap3: '',
  353. 'attack speed': (100 + stats.attackSpeed) + '%',
  354. 'cast speed': (100 + stats.castSpeed) + '%'
  355. },
  356. defense: {
  357. armor: stats.armor,
  358. 'chance to block attacks': stats.blockAttackChance + '%',
  359. 'chance to block spells': stats.blockSpellChance + '%',
  360. gap1: '',
  361. 'chance to dodge attacks': (~~(stats.dodgeAttackChance * 10) / 10) + '%',
  362. 'chance to dodge spells': (~~(stats.dodgeSpellChance * 10) / 10) + '%',
  363. gap2: '',
  364. 'arcane resist': stats.elementArcaneResist,
  365. 'fire resist': stats.elementFireResist,
  366. 'frost resist': stats.elementFrostResist,
  367. 'holy resist': stats.elementHolyResist,
  368. 'poison resist': stats.elementPoisonResist,
  369. gap3: '',
  370. 'all resist': stats.elementAllResist,
  371. gap4: '',
  372. 'life gained on hit': stats.lifeOnHit
  373. },
  374. misc: {
  375. 'item quality': stats.magicFind + '%',
  376. 'item quantity': stats.itemQuantity + '%',
  377. gap1: '',
  378. 'sprint chance': ((~~(stats.sprintChance * 100) / 100) || 0) + '%',
  379. gap2: '',
  380. 'xp increase': stats.xpIncrease + '%',
  381. gap3: '',
  382. 'chance to catch a fish': stats.catchChance + '%',
  383. 'fishing speed': stats.catchSpeed + '%',
  384. 'increased fish rarity': stats.fishRarity + '%',
  385. 'increased fish weight': stats.fishWeight + '%',
  386. 'chance to fish items': stats.fishItems + '%'
  387. }
  388. }[this.find('.tab.selected').html()];
  389. for (let s in newStats) {
  390. let label = s + ': ';
  391. let value = newStats[s];
  392. let isGap = false;
  393. if (label.indexOf('gap') === 0) {
  394. isGap = true;
  395. label = '';
  396. value = '';
  397. }
  398. let row = $('<div class="stat"><font class="q0">' + label + '</font><font color="#999">' + value + '</font></div>')
  399. .appendTo(container);
  400. if (s === 'gold')
  401. row.addClass('gold');
  402. else if ((s === 'level') || (s === 'next level'))
  403. row.addClass('blueText');
  404. if (isGap)
  405. row.addClass('empty');
  406. }
  407. }
  408. };
  409. });