Item quality checker for Orna.RPG

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

当前为 2022-09-04 提交的版本,查看 最新版本

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