Item quality checker for Orna.RPG

Let you easily calculate item quality in official Orna Codex page.

  1. // ==UserScript==
  2. // @name Item quality checker for Orna.RPG
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.2.2
  5. // @description Let you easily calculate item quality in official Orna Codex page.
  6. // @author RplusTW
  7. // @match https://playorna.com/codex/items/*/
  8. // @match https://playorna.com/codex/items/*/?*
  9. // @icon https://www.google.com/s2/favicons?sz=64&domain=playorna.com
  10. // @require https://cdn.jsdelivr.net/npm/lil-gui@0.17
  11. // @grant GM_registerMenuCommand
  12. // @grant GM_setValue
  13. // @grant GM_getValue
  14. // @run-at document-end
  15. // @license MIT
  16. // ==/UserScript==
  17.  
  18. let autoInit = GM_getValue('autoInit') || false;
  19.  
  20. GM_registerMenuCommand('Auto Init. ?', toggleAutoInit, 'A');
  21. function toggleAutoInit() {
  22. autoInit = window.confirm('Enable Auto initialize for debuff checker?')
  23. GM_setValue('autoInit', autoInit);
  24. }
  25.  
  26. window.addEventListener('load', async function() {
  27. 'use strict';
  28.  
  29. if (autoInit) {
  30. init();
  31. } else {
  32. document.querySelector('.codex-page-icon')?.addEventListener('dblclick', init, { once: true, });
  33. }
  34.  
  35. async function init() {
  36. var s = document.createElement('script');
  37. s.setAttribute('src', 'https://cdn.jsdelivr.net/npm/lil-gui@0.17');
  38. document.body.appendChild(s);
  39. s.onload = scriptOnload;
  40. }
  41. }, false);
  42.  
  43. async function scriptOnload() {
  44. let GUI = window.lil.GUI;
  45. let urlPath = location.href.match(/items\/([^/]+)/);
  46. let assessUrl = `https://orna.guide/search?searchstr=${urlPath?.[1].replace(/\W/g, ' ')}`;
  47. let gid = '';
  48. let data = {
  49. '%': 100,
  50. 'assess_guide': () => { window.open(assessUrl, 'guide'); },
  51. 'assess_api': () => { initAssess(gid); },
  52. }
  53.  
  54. let div = document.createElement('div');
  55. div.id = 'gui-div';
  56. let statsDiv = document.querySelector('.codex-stats');
  57. statsDiv.after(div);
  58. div.style = `display:flex;justify-content:center;flex-wrap:wrap;`;
  59. var gui = new GUI({
  60. autoPlace: false,
  61. container: div,
  62. });
  63. let stats = getStatValues(statsDiv);
  64. console.log({stats});
  65. stats?.forEach(stat => {
  66. data[stat.prop] = stat.value;
  67. gui.add(
  68. data, stat.prop,
  69. ~~(stat.value * 0.7),
  70. stat.value * 2,
  71. 1
  72. )
  73. .onChange(_v => {
  74. quality.setValue(~~(100 * _v / stat.value))
  75. });
  76. });
  77. let quality = gui.add(data, '%', 70, 200);
  78.  
  79. let assessFolder = gui.addFolder( 'Assess' );
  80. assessFolder.close();
  81.  
  82. let assessOnGuide = assessFolder.add(data, 'assess_guide').name(`🔍 on Orna.Guide `);
  83.  
  84. let itemInfo = await getItemInfo();
  85. gid = itemInfo.id;
  86. if (gid) {
  87. assessUrl = `https://orna.guide/items?show=${gid}`;
  88. assessOnGuide.name(`🔍 ${itemInfo.name} on Orna.Guide 🔗`);
  89.  
  90. let assessByAPI = assessFolder.add(data, 'assess_api').name(`Assess ${itemInfo.name} Here! 🌟`);
  91. }
  92. }
  93.  
  94. function getStatValues(statDom) {
  95. let statDivs = [...statDom.querySelectorAll('.codex-stat')];
  96. if (!statDivs?.length) {
  97. return null;
  98. }
  99.  
  100. return statDivs.map(div => {
  101. let info = div.textContent.trim().match(/(\D+)(\d+)/);
  102. let prop = info?.[1].trim().replace(':', '').toLowerCase();
  103. let value = +info?.[2];
  104. return {
  105. prop,
  106. value,
  107. };
  108. });
  109. }
  110.  
  111. function initAssess(gid) {
  112. if (window.assessInited) {
  113. return;
  114. }
  115. window.assessInited = true;
  116.  
  117. let divForm = document.createElement('div');
  118. let resultBox = document.createElement('details');
  119. resultBox.style = 'width:100%';
  120. let optionsHtml = ['level', 'attack', 'defense', 'magic', 'resistance', 'hp', 'mana', 'dexterity', 'ward', 'crit', ]
  121. .map(i => genLabel(i)).join('');
  122.  
  123.  
  124. divForm.innerHTML = `
  125. <details open>
  126. <form id="assess-form" class="lil-gui">
  127. ${genLabel('id', gid, 'readonly')}
  128. ${optionsHtml}
  129. <div class="controller">
  130. <div class="widget"><input type="submit"></div>
  131. <div class="widget"></div>
  132. <div class="widget"><input type="reset"></div>
  133. </div>
  134. </form>
  135. </details>`;
  136.  
  137. document.querySelector('#gui-div').appendChild(divForm);
  138. document.querySelector('#gui-div').appendChild(resultBox);
  139.  
  140. let form = divForm.querySelector('#assess-form');
  141. form.addEventListener('submit', (e) => {
  142. e.preventDefault();
  143. const formData = {};
  144. for (const pair of new FormData(form)) {
  145. if (pair[1]) {
  146. formData[pair[0]] = +pair[1];
  147. }
  148. }
  149.  
  150. fetch('https://orna.guide/api/v1/assess', {
  151. method: 'post',
  152. body: JSON.stringify(formData),
  153. }).then(r => r.json())
  154. .then(d => {
  155. resultBox.innerHTML = genAssessTable(d);
  156. resultBox.open = true;
  157. });
  158. });
  159. }
  160.  
  161. function genAssessTable(data) {
  162. let stats = data.stats;
  163. let props = Object.keys(stats);
  164. let title = document.querySelector('h1.herotext')?.textContent || '';
  165.  
  166. let ths = props.map(prop => genTd(prop, 'th')).join('');
  167.  
  168. let tbody = stats[props[0]].values.map((_i, index) => {
  169. let tds = props.map(prop => {
  170. return genTd(stats[prop].values[index]);
  171. }).join('');
  172. return `<tr>
  173. ${genTd(index + 1)}
  174. ${tds}
  175. </tr>`
  176. }).join('');
  177.  
  178. props.map(prop => {
  179. stats[prop].values
  180. });
  181.  
  182. return `
  183. <table style="margin:auto;text-transform:capitalize;">
  184. <caption>${title} ${data.quality * 100}%</caption>
  185. <style>details th {border-bottom:1px dotted #fff6;}</style>
  186. <tr>
  187. <th>Lv</th>
  188. ${ths}
  189. </tr>
  190. ${data.quality * 1 ? tbody : ''}
  191. </table>
  192. `;
  193. }
  194.  
  195. function genTd(str, tag = 'td') {
  196. return `<${tag}>${str}</${tag}>`;
  197. }
  198.  
  199. function genLabel(prop = '', value = '', attr) {
  200. return `
  201. <label class="controller number">
  202. <div class="name" style="text-transform:capitalize;">${prop}</div>
  203. <div class="widget">
  204. <input type="number" value="${value}" name="${prop}" ${attr} />
  205. </div>
  206. </label>`;
  207. }
  208.  
  209. async function getItemInfo() {
  210. let info = await getEnInfo();
  211. let name = info.title;
  212. if (!name) {
  213. return false;
  214. }
  215. let itemData = await postData('https://orna.guide/api/v1/item', {name});
  216. if (itemData?.length !== 1) {
  217. return false;
  218. }
  219. return {
  220. id: itemData[0]?.id,
  221. name,
  222. };
  223. }
  224.  
  225. function postData(url, data) {
  226. return fetch(url, {
  227. method: 'POST',
  228. body: JSON.stringify(data)
  229. }).then(res => res.json());
  230. }
  231.  
  232. function getEnURL() {
  233. let a = document.createElement('a');
  234. a.href = location.href;
  235. a.search = 'lang=en';
  236. a.href = `https://api.allorigins.win/raw?url=${encodeURIComponent(a.href)}`;
  237. // a.href = 'https://api.codetabs.com/v1/proxy?quest=' + a.href;
  238. // a.href = ' https://fast-dawn-89938.herokuapp.com/' + a.href;
  239. return a.href;
  240. }
  241.  
  242. async function getEnInfo() {
  243. let html = await fetch(getEnURL()).then(res => res.text());
  244. let doc = document.implementation.createHTMLDocument();
  245. doc.body.innerHTML = html;
  246. let h1 = doc.querySelector('h1.herotext');
  247. return {
  248. title: h1.textContent.trim(),
  249. // stats: getStatValues(doc.querySelector('.codex-stats')),
  250. };
  251. }