// ==UserScript==
// @name Monster debuff checker for Orna.RPG
// @namespace http://tampermonkey.net/
// @version 1.1.2
// @description Let you check monster's debuff in official Orna Codex page.
// @author RplusTW
// @match https://playorna.com/codex/raids/*/*
// @match https://playorna.com/codex/bosses/*/*
// @match https://playorna.com/codex/followers/*/*
// @match https://playorna.com/codex/monsters/*/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=playorna.com
// @require https://cdn.jsdelivr.net/npm/[email protected]
// @grant GM_registerMenuCommand
// @grant GM_setValue
// @grant GM_getValue
// @run-at document-end
// @license MIT
// ==/UserScript==
let autoInit = GM_getValue('autoInit') || false;
GM_registerMenuCommand('Auto Init. ?', toggleAutoInit, 'A');
function toggleAutoInit() {
autoInit = window.confirm('Enable Auto initialize for debuff checker?')
GM_setValue('autoInit', autoInit);
}
window.addEventListener('load', function() {
if (autoInit) {
init();
} else {
document.querySelector('.codex-page-icon')?.addEventListener('dblclick', init, { once: true, });
}
}, false);
async function init() {
let style = document.createElement('style');
style.textContent = `.cus-checker{opacity:.3}.cus-checker:checked{opacity:.75}.cus-checker:checked+*{opacity:.5}`;
document.head.append(style);
collapsePage();
let monster = await getEnInfo();
initEffects(monster.effects);
}
function collapsePage() {
let tags = [...document.querySelectorAll('.codex-page h4, .codex-page h4 ~ div')];
if (!tags.length) { return; }
let box = null;
let sections = tags.reduce((all, tag) => {
if (tag.tagName === 'H4') {
all[all.length] = [
tag,
[]
];
} else if (tag.tagName === 'DIV') {
all[all.length - 1][1].push(genDetailsItem('', tag.innerHTML));
tag.remove();
}
return all;
}, []);
sections.forEach(section => {
section[0].insertAdjacentHTML(
'beforebegin',
genDetailsWrapper(
genDetails(
section[0].textContent.trim(),
section[1].join('')
)
)
);
section[0].remove();
});
}
function initEffects(effects) {
let box = document.querySelector('.codex-page');
let html = '';
for (let prop in effects) {
// effects[prop] = slimEffects(effects[prop]);
html += genEffectHtml(prop, slimEffects(effects[prop]));
};
box.innerHTML += `<hr>${genDetailsWrapper(html)}`;
}
function genEffectHtml(prop, effects) {
let items = effects.map(eff => genDetailsItem(eff[0], `
<span>
${eff[0]},
<sub>${eff[1].join()}%</sub>
</span>
`)).join('');
return genDetails(prop, items);
}
function genDetailsItem(name, ctx) {
return `
<li>
<label>
<input type="checkbox" name="${name}" class="cus-checker">
${ctx}
</label>
</li>
`;
}
function genDetailsWrapper(html) {
return `<div style="display:flex;justify-content:space-evenly;flex-wrap:wrap;">${html}</div>`
}
function genDetails(title, listHtml) {
return `
<details open style="width:fit-content;">
<summary style="text-transform:capitalize;">
${title}
</summary>
<ul style="list-style:none;text-align:start;padding:0;">${listHtml}</ul>
</details>`
}
function slimEffects(effects) {
let eff = effects.reduce((all, e) => {
let o = e.match(/^(\D+)\s\((\d+)/);
all[o[1]] = all[o[1]] || [];
all[o[1]].push(+o[2]);
return all;
}, {});
return Object.keys(eff).map(prop => {
return [prop, [...new Set(eff[prop])].sort().reverse()];
}).sort((a, b) => a[0].localeCompare(b[0]));
return eff;
}
async function getEnInfo() {
let html = await getUrlSource(getURL());
let h1 = parseHtml(html, 'h1.herotext');
let data = itemParse(html);
let skillWord = skillWords.find(str => data[str]);
let skills = itemParse(html)[skillWord];
let effects = await parseSkillEffect(skills);
return {
title: h1[0].textContent.trim(),
skills,
effects,
};
}
async function parseSkillEffect(skills) {
// getURL()
let sources = await Promise.all(
skills.map( skill => getUrlSource(getURL(skill.url)) )
);
let effects = skills.reduce((all, skill, index) => {
skill.effect = itemParse(sources[index]);
for (let prop in skill.effect) {
if (!all[prop]) {
all[prop] = [];
}
let _es = skill.effect[prop].map(e => e.title);
all[prop] = all[prop].concat(_es);
}
return all;
}, {});
return effects;
}
async function getUrlSource(url) {
return fetch(url).then(res => res.text());
}
function parseHtml(html, selectoor = '') {
let doc = document.implementation.createHTMLDocument();
doc.body.innerHTML = html;
return [...doc.querySelectorAll(selectoor)];
}
function itemParse(html) {
let dataDivs = parseHtml(html, '.codex-page h4, .codex-page h4 ~ div');
let data = dataDivs.reduce((all, div) => {
if (div.tagName === 'H4') {
let _prop = div.textContent.replace(/[::]/, '').trim().toLowerCase();
all.currentProp = _prop;
all[_prop] = all[_prop] || [];
} else if (div.tagName === 'DIV') {
let icon = div.querySelector('img')?.src;
all[all.currentProp].push({
icon: div.querySelector('img')?.src,
url: div.querySelector('a')?.href,
title: div.textContent.trim(),
});
}
return all;
}, {});
delete data.currentProp;
return data;
}
function getURL(url = location.href, lang = 'en') {
return url;
// let a = document.createElement('a');
// a.href = url;
// a.search = `lang=${lang}`;
// a.href = 'https://api.codetabs.com/v1/proxy?quest=' + a.href;
// return a.href;
}
const skillWords = [
"Skills",
"Compétences ",
"Habilidades",
"Fähigkeiten",
"Умения",
"技能",
"Umiejętności",
"Készségek",
"Навички",
"Abilità",
"스킬",
"スキル"
].map(str => str.toLowerCase());