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.
 
 
 

397 lines
8.2 KiB

  1. define([
  2. 'js/misc/statTranslations',
  3. 'ui/templates/tooltipItem/buildTooltip/stringifyStatValue'
  4. ], function (
  5. statTranslations,
  6. stringifyStatValue
  7. ) {
  8. let item = null;
  9. let compare = null;
  10. let shiftDown = null;
  11. let equipErrors = null;
  12. const init = (_item, _compare, _shiftDown, _equipErrors) => {
  13. item = _item;
  14. compare = _compare;
  15. shiftDown = _shiftDown;
  16. equipErrors = _equipErrors;
  17. };
  18. const lineBuilders = {
  19. div: (className, children) => {
  20. if (!children)
  21. return null;
  22. if (children.join)
  23. children = children.join('');
  24. if (!children.length)
  25. return null;
  26. return `<div class="${className}">${children}</div>`;
  27. },
  28. name: () => {
  29. let itemName = item.name;
  30. if (item.quantity > 1)
  31. itemName += ' x' + item.quantity;
  32. return itemName;
  33. },
  34. type: () => {
  35. if (!item.type || item.type === item.name)
  36. return null;
  37. return item.type;
  38. },
  39. power: () => {
  40. if (!item.power)
  41. return null;
  42. return (new Array(item.power + 1)).join('+');
  43. },
  44. implicitStats: () => {
  45. if (!item.implicitStats)
  46. return null;
  47. const tempImplicitStats = $.extend(true, [], item.implicitStats);
  48. if (compare && shiftDown && !item.eq) {
  49. const compareImplicitStats = (compare.implicitStats || []);
  50. tempImplicitStats.forEach(s => {
  51. const { stat, value } = s;
  52. const f = compareImplicitStats.find(c => c.stat === stat);
  53. if (!f) {
  54. s.value = `+${value}`;
  55. return;
  56. }
  57. const delta = value - f.value;
  58. if (delta > 0)
  59. s.value = `+${delta}`;
  60. else if (delta < 0)
  61. s.value = delta;
  62. });
  63. compareImplicitStats.forEach(s => {
  64. if (tempImplicitStats.some(f => f.stat === s.stat))
  65. return;
  66. tempImplicitStats.push({
  67. stat: s.stat,
  68. value: -s.value
  69. });
  70. });
  71. }
  72. const html = tempImplicitStats
  73. .map(({ stat, value }) => {
  74. let statName = statTranslations.translate(stat);
  75. const prettyValue = stringifyStatValue(stat, value);
  76. let rowClass = '';
  77. if (compare) {
  78. if (prettyValue.indexOf('-') > -1)
  79. rowClass = 'loseStat';
  80. else if (prettyValue.indexOf('+') > -1)
  81. rowClass = 'gainStat';
  82. }
  83. return `<div class="${rowClass}">${prettyValue} ${statName}</div>`;
  84. })
  85. .join('');
  86. const result = (
  87. lineBuilders.div('space', ' ') +
  88. html
  89. );
  90. return result;
  91. },
  92. stats: () => {
  93. const tempStats = $.extend(true, {}, item.stats);
  94. const enchantedStats = item.enchantedStats || {};
  95. if (compare && shiftDown) {
  96. if (!item.eq) {
  97. const compareStats = compare.stats;
  98. for (let s in tempStats) {
  99. if (compareStats[s]) {
  100. const delta = tempStats[s] - compareStats[s];
  101. if (delta > 0)
  102. tempStats[s] = '+' + delta;
  103. else if (delta < 0)
  104. tempStats[s] = delta;
  105. } else
  106. tempStats[s] = '+' + tempStats[s];
  107. }
  108. for (let s in compareStats) {
  109. if (!tempStats[s])
  110. tempStats[s] = -compareStats[s];
  111. }
  112. }
  113. } else {
  114. Object.keys(tempStats).forEach(s => {
  115. if (enchantedStats[s]) {
  116. tempStats[s] -= enchantedStats[s];
  117. if (tempStats[s] <= 0)
  118. delete tempStats[s];
  119. tempStats['_' + s] = enchantedStats[s];
  120. }
  121. });
  122. }
  123. const html = Object.keys(tempStats)
  124. .map(s => {
  125. const isEnchanted = (s[0] === '_');
  126. let statName = s;
  127. if (isEnchanted)
  128. statName = statName.substr(1);
  129. const prettyValue = stringifyStatValue(statName, tempStats[s]);
  130. statName = statTranslations.translate(statName);
  131. let rowClass = '';
  132. if (compare) {
  133. if (prettyValue.indexOf('-') > -1)
  134. rowClass = 'loseStat';
  135. else if (prettyValue.indexOf('+') > -1)
  136. rowClass = 'gainStat';
  137. }
  138. if (isEnchanted)
  139. rowClass += ' enchanted';
  140. return `<div class="${rowClass}">${prettyValue} ${statName}</div>`;
  141. })
  142. .sort((a, b) => {
  143. return (a.replace(' enchanted', '').length - b.replace(' enchanted', '').length);
  144. })
  145. .sort((a, b) => {
  146. if (a.indexOf('enchanted') > -1 && b.indexOf('enchanted') === -1)
  147. return 1;
  148. else if (a.indexOf('enchanted') === -1 && b.indexOf('enchanted') > -1)
  149. return -1;
  150. return 0;
  151. })
  152. .join('');
  153. if (!html)
  154. return null;
  155. const result = (
  156. lineBuilders.div('space', ' ') +
  157. lineBuilders.div('line', ' ') +
  158. lineBuilders.div('smallSpace', ' ') +
  159. html +
  160. lineBuilders.div('smallSpace', ' ') +
  161. lineBuilders.div('line', ' ')
  162. );
  163. return result;
  164. },
  165. effects: () => {
  166. if (!item.effects || !item.effects.length || !item.effects[0].text || item.type === 'mtx')
  167. return null;
  168. let html = '';
  169. item.effects.forEach((e, i) => {
  170. html += e.text;
  171. if (i < item.effects.length - 1)
  172. html += '<br />';
  173. });
  174. const result = (
  175. lineBuilders.div('space', ' ') +
  176. lineBuilders.div('line', ' ') +
  177. lineBuilders.div('smallSpace', ' ') +
  178. html +
  179. lineBuilders.div('smallSpace', ' ') +
  180. lineBuilders.div('line', ' ')
  181. );
  182. return result;
  183. },
  184. material: () => {
  185. if (item.material)
  186. return 'crafting material';
  187. },
  188. quest: () => {
  189. if (item.quest)
  190. return 'quest item';
  191. },
  192. spellName: () => {
  193. if (!item.spell || item.ability)
  194. return null;
  195. return (
  196. lineBuilders.div('space', ' ') +
  197. lineBuilders.div(`spellName q${item.spell.quality}`, item.spell.name)
  198. );
  199. },
  200. damage: () => {
  201. if (!item.spell || !item.spell.values)
  202. return null;
  203. const abilityValues = Object.entries(item.spell.values)
  204. .map(([k, v]) => {
  205. if (!compare || !shiftDown)
  206. return `${k}: ${v}<br/>`;
  207. let delta = v - compare.spell.values[k];
  208. // adjust by EPSILON to handle float point imprecision, otherwise 3.15 - 2 = 1.14 or 2 - 3.15 = -1.14
  209. // have to move away from zero by EPSILON, not a simple add
  210. if (delta >= 0)
  211. delta += Number.EPSILON;
  212. else
  213. delta -= Number.EPSILON;
  214. delta = ~~((delta) * 100) / 100;
  215. let rowClass = '';
  216. if (delta > 0) {
  217. rowClass = 'gainDamage';
  218. delta = '+' + delta;
  219. } else if (delta < 0)
  220. rowClass = 'loseDamage';
  221. return `<div class="${rowClass}">${k}: ${delta}</div>`;
  222. })
  223. .join('');
  224. return abilityValues;
  225. },
  226. requires: (className, children) => {
  227. if (!item.requires && !item.level && (!item.factions || !item.factions.length))
  228. return null;
  229. if (equipErrors.length)
  230. className += ' high-level';
  231. return (
  232. lineBuilders.div('space', ' ') +
  233. lineBuilders.div(className, 'requires')
  234. );
  235. },
  236. requireLevel: className => {
  237. if (!item.level)
  238. return null;
  239. if (equipErrors.includes('level'))
  240. className += ' high-level';
  241. const level = item.level.push ? `${item.level[0]} - ${item.level[1]}` : item.level;
  242. return lineBuilders.div(className, `level: ${level}`);
  243. },
  244. requireStats: className => {
  245. if (!item.requires || !item.requires[0])
  246. return null;
  247. if (equipErrors.includes('stats'))
  248. className += ' high-level';
  249. let html = `${item.requires[0].stat}: ${item.requires[0].value}`;
  250. return lineBuilders.div(className, html);
  251. },
  252. requireFaction: () => {
  253. if (!item.factions)
  254. return null;
  255. let htmlFactions = '';
  256. item.factions.forEach((f, i) => {
  257. let htmlF = f.name + ': ' + f.tierName;
  258. if (f.noEquip)
  259. htmlF = '<font class="color-red">' + htmlF + '</font>';
  260. htmlFactions += htmlF;
  261. if (i < item.factions.length - 1)
  262. htmlFactions += '<br />';
  263. });
  264. return htmlFactions;
  265. },
  266. worth: () => {
  267. if (!item.worthText)
  268. return null;
  269. return (
  270. lineBuilders.div('space', ' ') +
  271. `value: ${item.worthText}`
  272. );
  273. },
  274. info: () => {
  275. if (!item.slot)
  276. return null;
  277. let text = null;
  278. if (isMobile && compare && !shiftDown)
  279. text = 'tap again to compare';
  280. else if (!shiftDown && compare)
  281. text = '[shift] to compare';
  282. if (!text)
  283. return null;
  284. return (
  285. lineBuilders.div('space', ' ') +
  286. text
  287. );
  288. },
  289. description: () => {
  290. if (!item.description)
  291. return null;
  292. return (
  293. lineBuilders.div('space', ' ') +
  294. item.description
  295. );
  296. },
  297. cd: () => {
  298. if (!item.cd)
  299. return null;
  300. return `cooldown: ${item.cd}`;
  301. },
  302. uses: () => {
  303. if (!item.uses)
  304. return null;
  305. return `uses: ${item.uses}`;
  306. }
  307. };
  308. return {
  309. init,
  310. lineBuilders
  311. };
  312. });