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.
 
 
 

396 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. (!!item.spell?.values ? lineBuilders.div('line', ' ') + lineBuilders.div('space', ' ') : '') +
  177. html +
  178. lineBuilders.div('space', ' ') +
  179. lineBuilders.div('line', ' ')
  180. );
  181. return result;
  182. },
  183. material: () => {
  184. if (item.material)
  185. return 'crafting material';
  186. },
  187. quest: () => {
  188. if (item.quest)
  189. return 'quest item';
  190. },
  191. spellName: () => {
  192. if (!item.spell || item.ability)
  193. return null;
  194. return (
  195. lineBuilders.div('space', ' ') +
  196. lineBuilders.div(`spellName q${item.spell.quality}`, item.spell.name)
  197. );
  198. },
  199. damage: () => {
  200. if (!item.spell || !item.spell.values)
  201. return null;
  202. const abilityValues = Object.entries(item.spell.values)
  203. .map(([k, v]) => {
  204. if (!compare || !shiftDown)
  205. return `${k}: ${v}<br/>`;
  206. let delta = v - compare.spell.values[k];
  207. // adjust by EPSILON to handle float point imprecision, otherwise 3.15 - 2 = 1.14 or 2 - 3.15 = -1.14
  208. // have to move away from zero by EPSILON, not a simple add
  209. if (delta >= 0)
  210. delta += Number.EPSILON;
  211. else
  212. delta -= Number.EPSILON;
  213. delta = ~~((delta) * 100) / 100;
  214. let rowClass = '';
  215. if (delta > 0) {
  216. rowClass = 'gainDamage';
  217. delta = '+' + delta;
  218. } else if (delta < 0)
  219. rowClass = 'loseDamage';
  220. return `<div class="${rowClass}">${k}: ${delta}</div>`;
  221. })
  222. .join('');
  223. return abilityValues;
  224. },
  225. requires: (className, children) => {
  226. if (!item.requires && !item.level && (!item.factions || !item.factions.length))
  227. return null;
  228. if (equipErrors.length)
  229. className += ' high-level';
  230. return (
  231. lineBuilders.div('space', ' ') +
  232. lineBuilders.div(className, 'requires')
  233. );
  234. },
  235. requireLevel: className => {
  236. if (!item.level)
  237. return null;
  238. if (equipErrors.includes('level'))
  239. className += ' high-level';
  240. const level = item.level.push ? `${item.level[0]} - ${item.level[1]}` : item.level;
  241. return lineBuilders.div(className, `level: ${level}`);
  242. },
  243. requireStats: className => {
  244. if (!item.requires || !item.requires[0])
  245. return null;
  246. if (equipErrors.includes('stats'))
  247. className += ' high-level';
  248. let html = `${item.requires[0].stat}: ${item.requires[0].value}`;
  249. return lineBuilders.div(className, html);
  250. },
  251. requireFaction: () => {
  252. if (!item.factions)
  253. return null;
  254. let htmlFactions = '';
  255. item.factions.forEach((f, i) => {
  256. let htmlF = f.name + ': ' + f.tierName;
  257. if (f.noEquip)
  258. htmlF = '<font class="color-red">' + htmlF + '</font>';
  259. htmlFactions += htmlF;
  260. if (i < item.factions.length - 1)
  261. htmlFactions += '<br />';
  262. });
  263. return htmlFactions;
  264. },
  265. worth: () => {
  266. if (!item.worthText)
  267. return null;
  268. return (
  269. lineBuilders.div('space', ' ') +
  270. `value: ${item.worthText}`
  271. );
  272. },
  273. info: () => {
  274. if (!item.slot)
  275. return null;
  276. let text = null;
  277. if (isMobile && compare && !shiftDown)
  278. text = 'tap again to compare';
  279. else if (!shiftDown && compare)
  280. text = '[shift] to compare';
  281. if (!text)
  282. return null;
  283. return (
  284. lineBuilders.div('space', ' ') +
  285. text
  286. );
  287. },
  288. description: () => {
  289. if (!item.description)
  290. return null;
  291. return (
  292. lineBuilders.div('space', ' ') +
  293. item.description
  294. );
  295. },
  296. cd: () => {
  297. if (!item.cd)
  298. return null;
  299. return `cooldown: ${item.cd}`;
  300. },
  301. uses: () => {
  302. if (!item.uses)
  303. return null;
  304. return `uses: ${item.uses}`;
  305. }
  306. };
  307. return {
  308. init,
  309. lineBuilders
  310. };
  311. });