`
rankingsDiv.setAttribute('class','widget-content widget-content-area text-left mb-3');
menuLeft.insertBefore(rankingsDiv, firstLeftChild);
const equipmentDiv = document.createElement('div');
equipmentDiv.innerHTML = equipmentDivContent
equipmentDiv.setAttribute('class','widget mb-3');
menuRight.insertBefore(equipmentDiv, firstRightChild);
const rankingsEle = await superfetch('ajax/rankings.php?type=char_elepower')
const rankingsPower = await superfetch('ajax/rankings.php?type=char_power')
const rankingsChaos = await superfetch('ajax/rankings.php?type=char_chaos')
const rankingsMr = await superfetch('ajax/rankings.php?type=char_maxRage')
// Return if logged in via trustee
if (rankingsEle == "error"){
document.querySelector("#homeTilesContainer").innerHTML = 'Alert tiles not available via trustee'
return;
};
const arrayEle = rankingsEle.match(/\{"id":"[0-9]+","name":"[^"]*","pic":"[^"]*","stat":[0-9]+,/g)
const arrayPower = rankingsPower.match(/\{"id":"[0-9]+","name":"[^"]*","stat":"[0-9]+","pic":"[^"]*",/g)
const arrayChaos = rankingsChaos.match(/\{"id":"[0-9]+","name":"[^"]*","pic":"[^"]*","stat":[0-9]+/g)
const arrayMr = rankingsMr.match(/"id":"[0-9]+","name":"[^"]*","stat":[0-9]+,"pic":"[^"]*"/g)
let tableEle = ''
let tablePower = ''
let tableChaos = ''
let tableMr = ''
for (let i = 0; i < 10; i++) {
const obj = arrayEle[i];
const id = obj.match(/"id":"([0-9]+)"/i)[1]
const pic = obj.match(/"pic":"([^"]*)"/i)[1]
const name = i == 0 ? `${obj.match(/"name":"([^"]*)"/i)[1]}
` : obj.match(/"name":"([^"]*)"/i)[1]
const stat = parseInt(obj.match(/"stat":([0-9]+)/i)[1]).toLocaleString()
if (obj.match(/"name":"([^"]*)"/i)[1] == charName){
tableEle += `
`);
};
await Promise.all(array.map(tableRows));
document.querySelector("#mvRgaSelectDiv").innerHTML = `
`
// Load greatness button function
document.querySelector("#mvStart").addEventListener('click', async function(){
const rows = document.querySelectorAll('.rga-selected');
const array = [];
// Throw error if multiple RGAs available and none are selected
if (rows.length == 0){
alert ('Please select at least 1 RGA');
// Load local RGA if it's the only one available
} else {
this.outerHTML = '
(.*?)<\/td> (.*?)<\/td> (.*?)<\/td> .*?<\/td><\/tr>/i)
const profileData = await superfetchProfile(`profile?id=${string[1]}`);
const character = profileData.name;
const level = profileData.level;
// Rgas array format [login string,rga name,char id,char name, level]
rgas.push([`rg_sess_id=${string[2]}&suid=${string[1]}&serverid=${serverNo}`,string[3],string[1],character,level]);
});
// Remove rga column if only 1 rga is selected
if (rows.length == 1){
GM_addStyle(`.mv-rga-cell{display:none;}`)
};
// Load moxxivision
await mv(rgas);
};
});
};
// RGA row selection
const rows = document.querySelectorAll('table.mv-rga-select-table tbody tr');
rows.forEach(tr => {
tr.addEventListener('click', async function (){
this.classList.toggle('rga-selected');
});
});
// Core moxxivision function
async function mv(rgas){
// Build header buttons and HTML content
document.querySelector("#blankOverlay").outerHTML = `
`
// Set position of #content
var heightHeader = document.querySelector("#mvHeader").clientHeight + 10;
document.querySelector("#mvContent").setAttribute('style',`top:${heightHeader}px`)
// Open panel buttones
const open = document.querySelectorAll(".open-btn")
open.forEach(function(button){
button.addEventListener('click',async function(){
const alt = this.outerHTML.match(/alt="([^"]*)"/i)[1];
var currentPosition = document.getElementById(`div-${alt}`).getBoundingClientRect();
document.getElementById(`div-${alt}`).style.top = currentPosition.top + 200 + 'px';
// Move the mvContent div
var heightDiv = document.getElementById(`div-${alt}`).clientHeight;
document.querySelector("#mvContent").style.top = heightDiv + 10 + 'px';
// Set mvContent height when toolbar adjusts
await new Promise(resolve => setTimeout(resolve, 500));
const vh = window.innerHeight;
document.querySelector('#mvContent').style.height = '';
});
});
// Close panel buttons
const close = document.querySelectorAll(".close-btn")
close.forEach(function(button){
button.addEventListener('click',async function(){
const div = this.parentElement.parentElement.outerHTML.match(/id="([^"]*)"/i)[1];
var currentPosition = document.getElementById(div).getBoundingClientRect();
document.getElementById(div).style.top = currentPosition.top - 200 + 'px';
// Move the mvContent div
var heightHeader = document.querySelector("#mvHeader").clientHeight + 10;
document.querySelector("#mvContent").style.top = heightHeader + "px";
// Set mvContent height when toolbar adjusts
const vh = window.innerHeight;
document.querySelector('#mvContent').style.height = vh - heightHeader - 10 + 'px';
});
});
// All moxxivision tab buttons
document.querySelector("#character-overview").addEventListener('click', async function(){
await mvCharacterOverview(rgas)
});
document.querySelector("#character-stats").addEventListener('click', async function(){
await mvCharacterStats(rgas)
});
document.querySelector("#character-factions").addEventListener('click', async function(){
await mvCharacterFactions(rgas)
});
document.querySelector("#character-collections").addEventListener('click', async function(){
await mvCharacterCollections(rgas)
});
document.querySelector("#character-skills").addEventListener('click', async function(){
await mvCharacterSkills(rgas)
});
document.querySelector("#character-underlings").addEventListener('click', async function(){
await mvCharacterUnderlings(rgas)
});
document.querySelector("#character-god-slayer").addEventListener('click', async function(){
await mvCharacterGodSlayer(rgas)
});
document.querySelector("#character-codex").addEventListener('click', async function(){
await mvCharacterCodex(rgas)
});
document.querySelector("#equipment-equipped").addEventListener('click', async function(){
await mvEquipmentEquipped(rgas)
});
const slots = Array.from(document.querySelectorAll(".eq-slot"));
for (let i = 0; i < slots.length; i++) {
slots[i].addEventListener('click',async function(){
const slot = this.innerHTML.toLowerCase();
await mvEquipmentSlot(rgas,slot);
});
};
document.querySelector("#equipment-chaos-gem").addEventListener('click', async function(){
await mvEquipmentChaosGem(rgas)
});
document.querySelector("#equipment-badge").addEventListener('click',async function(){
await mvEquipmentBadge(rgas)
});
document.querySelector("#equipment-orbs").addEventListener('click',async function(){
await mvEquipmentOrbs(rgas)
});
document.querySelector("#equipment-rune").addEventListener('click',async function(){
await mvEquipmentRune(rgas)
});
document.querySelector("#equipment-booster").addEventListener('click',async function(){
await mvEquipmentBooster(rgas)
});
document.querySelector("#equipment-crests").addEventListener('click',async function(){
await mvEquipmentCrests(rgas)
});
document.querySelector("#equipment-augments").addEventListener('click',async function(){
await mvEquipmentAugments(rgas)
});
document.querySelector("#storage-backpack").addEventListener('click', async function(){
await mvStorageBackpack(rgas)
});
const bpType = Array.from(document.querySelectorAll(".bp-tab"));
for (let i = 0; i < bpType.length; i++) {
bpType[i].addEventListener('click', async function(){
const tab = this.outerHTML.match(/alt="([^"]*)"/i)[1];
await mvStorageItems(rgas,tab);
});
};
document.querySelector("#storage-equipment").addEventListener('click', async function(){
await mvStorageEquipment(rgas)
});
document.querySelector("#storage-vault").addEventListener('click', async function(){
await mvStorageVault(rgas)
});
document.querySelector("#storage-augments").addEventListener('click', async function(){
await mvStorageAugments(rgas)
});
const tabs = Array.from(document.querySelectorAll(".mv-plus"));
for (let i = 0; i < tabs.length; i++) {
tabs[i].addEventListener('click',async function(){
const string = await mmplus('AuthCheck|rganame');
const tab = this.outerHTML.match(/id="plus-([^"]*)"/i)[1];
if (tab == "events"){
const events = Array.from(document.querySelectorAll(".woz-top"));
for (let i = 0; i < events.length; i++) {
events[i].addEventListener('click',async function(){
const event = this.innerHTML;
await mvEvents(rgas,event,string);
});
};
document.querySelector("#plus-events-halloween").addEventListener('click', async function(){ await mvHalloween(rgas,string) });
document.querySelector("#plus-events-christmas").addEventListener('click', async function(){ await mvChristmas(rgas,string) });
}
else if (tab == "item-slotting"){
await mvItemSlotting(rgas,string);
}
else if (tab == "max-rage"){
await mvMaxRage(rgas,string);
}
else if (tab == "augment-data"){
await mvAugmentData(rgas,string);
};
});
};
};
};
// Moxxivision tabs
async function mvCharacterOverview(rgas){
const endpoints = ['profile','supplies'];
const array = await mvFetch(rgas,endpoints);
let totMr = 0;
let totPower = 0;
let totEle = 0;
let totChaos = 0;
let totRpt = 0;
let totSupplies = 0;
const rows = [];
const table = async (i) => {
const charclass = i.profile.class;
const crew = i.profile.crewname;
const rage = i.profile.currentrage.toLocaleString();
const exp = i.profile.exp.toLocaleString();
const tolvl = i.profile.tolevel.toLocaleString();
const today = i.profile.growthtoday.toLocaleString();
const yesterday = i.profile.growthyesterday.toLocaleString();
const strength = i.profile.strength;
const supplies = parseInt(i.supplies.replace(/\s/g,'').match(/([0-9]+)%<\/td>/i)[1])
const gold = i.profile.gold.toLocaleString();
rows.push(`${i.static}${charclass} ${crew} ${rage} ${exp} ${tolvl} ${today} ${yesterday} ${strength} ${supplies} ${gold} `);
// Totals
totMr += i.profile.maxrage;
totPower += i.profile.power;
totEle += i.profile.elemental;
totChaos += i.profile.chaos;
totRpt += i.profile.rageperturn;
totSupplies += parseInt(i.supplies.replace(/\s/g,'').match(/([0-9]+)%<\/td>/i)[1])
};
await Promise.all(array.map(table));
const avgSupplies = (totSupplies/array.length).toFixed(2);
// Build HTML
document.querySelector("#mvContent").innerHTML = `
TOTAL POWER
TOTAL ELEMENTAL
TOTAL CHAOS
TOTAL RPT
TOTAL MAX RAGE
SUPPLIES (MAX ALL )
${totPower.toLocaleString()}
${totEle.toLocaleString()}
${totChaos.toLocaleString()}
${totRpt.toLocaleString()}
${totMr.toLocaleString()}
${avgSupplies}%
char rga lvl class crew rage experience to level growth today yesterday strength supplies gold
${rows.join('')}
`
// Max all supplies function
document.querySelector("#maxAllSuppliesLink").addEventListener('click', async function(){
// Loading
const loadingDiv = document.createElement("div");
loadingDiv.innerHTML = `loading⧖ `
loadingDiv.id = "loadingDiv"
document.body.appendChild(loadingDiv);
// Max supplies
const maxAllSupplies = async (i) => {
const string = i.static.match(/href="profile\?(rg_sess_id=[A-Za-z0-9]+&suid=[0-9]+&serverid=[0-9]+)"/i)[1];
await fetch(`supplies?${string}`, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({'buymax': 'Buy Max',})})
};
await Promise.all(array.map(maxAllSupplies));
document.querySelector("#avgSupplies").innerHTML = "MAXED"
document.querySelector("#loadingDiv").remove();
});
await sortableTables();
document.querySelector("#loadingDiv").remove();
};
async function mvCharacterStats(rgas){
const endpoints = ['profile','home'];
const array = await mvFetch(rgas,endpoints);
const rows = [];
const table = async (i) => {
const mr = i.profile.maxrage.toLocaleString();
const power = i.profile.power.toLocaleString();
const ele = i.profile.elemental.toLocaleString();
const atk = i.profile.attack.toLocaleString();
const hp = i.profile.hp.toLocaleString();
const chaos = i.profile.chaos.toLocaleString();
const res1 = i.home.holyresist;
const res2 = i.home.arcaneresist;
const res3 = i.home.shadowresist;
const res4 = i.home.fireresist;
const res5 = i.home.kineticresist;
const rpt = i.profile.rageperturn.toLocaleString();
const ept = i.profile.expperturn.toLocaleString();
const wilderness = i.profile.wilderness.toLocaleString();
const mrrptratio = i.profile.maxrage/i.profile.rageperturn
rows.push(`
${i.static}
${mr.toLocaleString()}
${power}
${ele}
${atk}
${hp}
${chaos}
${res1}
${res2}
${res3}
${res4}
${res5}
${rpt.toLocaleString()}
${mrrptratio.toFixed(1)}
${ept}
${wilderness}
`);
};
await Promise.all(array.map(table));
document.querySelector("#mvContent").innerHTML = `
char rga lvl max rage power ele atk hp chaos res res res res res rpt mr:rpt ept wilderness
${rows.join('')}
`
await sortableTables();
document.querySelector("#loadingDiv").remove();
};
async function mvCharacterFactions(rgas){
const endpoints = ['profile','home'];
const array = await mvFetch(rgas,endpoints);
const rows = [];
const table = async (i) => {
const faction = i.profile.faction;
const a = i.home.alvar;
const d = i.home.delruk;
const v = i.home.vordyn;
const c = i.home.codex;
const t = a + d + v
rows.push(`
${i.static}
${faction}
${a}
${d}
${v}
${t}
${c}
`);
};
await Promise.all(array.map(table));
document.querySelector("#mvContent").innerHTML = `
char rga lvl current faction alvar loyalty delruk loyalty vordyn loyalty total loyalty codex level
${rows.join('')}
`
await sortableTables();
document.querySelector("#loadingDiv").remove();
};
async function mvCharacterSkills(rgas){
const endpoints = ['home','cast_skills?C=7','profile'];
const array = await mvFetch(rgas,endpoints);
const rows = [];
const table = async (i) => {
const wall = i.skills.match(/alt="Shield Wall"/i) ? "X" : "";
const slayer = i.skills.match(/alt="God Slayer"/i) ? "X" : "";
const grind = i.skills.match(/alt="Daily Grind"/i) ? "X" : "";
const influence = i.skills.match(/alt="Triworld Influence"/i) ? "X" : "";
const skillclass = i.home.skillclass;
const skillpoints = i.home.skillpoints;
const active = i.profile.skills.images;
rows.push(`${i.static}${skillclass} ${skillpoints} ${wall} ${slayer} ${grind} ${influence} ${active.join('')} `);
};
await Promise.all(array.map(table));
document.querySelector("#mvContent").innerHTML = `
char rga lvl class skill points active skills
${rows.join('')}
`
await sortableTables();
document.querySelector("#loadingDiv").remove();
};
async function mvCharacterUnderlings(rgas){
const endpoints = ['profile','home'];
const array = await mvFetch(rgas,endpoints);
const rows = [];
const table = async (i) => {
const lings = i.profile.underlings;
const loyalty = i.home.underlingloyalty * 10;
const allLings = i.profile.underlingids;
let allLingsAtk = 0;
let allLingsHp = 0;
let allLingsPower = 0;
let allLingsExp = 0;
if (allLings){
const lingLoop = async (lingid) => {
const id = lingid
const lingProfile = await superfetchProfile(`profile?id=${id}`);
allLingsAtk += lingProfile.attack;
allLingsHp += lingProfile.hp;
allLingsPower += lingProfile.power;
allLingsExp += lingProfile.exp;
};
await Promise.all(allLings.map(lingLoop));
}
const maxAtk = allLingsAtk >= 11236 ? "100%" : (allLingsAtk/11236*100).toFixed(1) + "%"
const maxHp = allLingsHp >= 25545 ? "100%" : (allLingsHp/25545*100).toFixed(1) + "%"
rows.push(`${i.static}${lings} ${allLingsExp.toLocaleString()} ${allLingsPower.toLocaleString()} ${loyalty} ${maxAtk} ${maxHp} `);
};
await Promise.all(array.map(table));
document.querySelector("#mvContent").innerHTML = `
char rga lvl lings total experience total power loyalty enhancement ATK % MAXED BUFF HP % MAXED BUFF
${rows.join('')}
`
await sortableTables();
document.querySelector("#loadingDiv").remove();
};
async function mvCharacterGodSlayer(rgas){
const endpoints = ['profile'];
const array = await mvFetch(rgas,endpoints);
const allgods = await info("Array of all gods");
const rows = [];
const table = async (i) => {
const crew = i.profile.crewname;
const completed = i.profile.completedgodslayer.split(',')
const needed = allgods.filter(item => !completed.includes(item));
const list = needed.map(i => i.replace(/(,| ,| the|The| of).*/gi, ''));
rows.push(`${i.static}${crew} ${i.profile.godslayer} ${list.join(', ')} `);
};
await Promise.all(array.map(table));
document.querySelector("#mvContent").innerHTML = `
`
await filterTables();
await sortableTables();
document.querySelector("#loadingDiv").remove();
};
async function mvCharacterCollections(rgas){
const endpoints = ['collections'];
const array = await mvFetch(rgas,endpoints);
const rows = [];
const table = async (i) => {
const parser = new DOMParser()
const x = parser.parseFromString(i.collections, 'text/html');
const anjou = (x.querySelector("#divCollections > div.row > div:nth-child(1) > div > div > div.user-info.w-100.pr-3 > ul").innerHTML.match(/img/g) || []).length + " / 3"
const reikar = (x.querySelector("#divCollections > div.row > div:nth-child(2) > div > div > div.user-info.w-100.pr-3 > ul").innerHTML.match(/img/g) || []).length + " / 3"
const lorren = (x.querySelector("#divCollections > div.row > div:nth-child(3) > div > div > div.user-info.w-100.pr-3 > ul").innerHTML.match(/img/g) || []).length + " / 3"
const lucile = (x.querySelector("#divCollections > div.row > div:nth-child(4) > div > div > div.user-info.w-100.pr-3 > ul").innerHTML.match(/img/g) || []).length + " / 3"
const weima = (x.querySelector("#divCollections > div.row > div:nth-child(5) > div > div > div.user-info.w-100.pr-3 > ul").innerHTML.match(/img/g) || []).length + " / 3"
const souma = (x.querySelector("#divCollections > div.row > div:nth-child(6) > div > div > div.user-info.w-100.pr-3 > ul").innerHTML.match(/img/g) || []).length + " / 3"
const vanisha = (x.querySelector("#divCollections > div.row > div:nth-child(7) > div > div > div.user-info.w-100.pr-3 > ul").innerHTML.match(/img/g) || []).length + " / 3"
const drolba = (x.querySelector("#divCollections > div.row > div:nth-child(8) > div > div > div.user-info.w-100.pr-3 > ul").innerHTML.match(/img/g) || []).length + " / 3"
const quibel = (x.querySelector("#divCollections > div.row > div:nth-child(9) > div > div > div.user-info.w-100.pr-3 > ul").innerHTML.match(/img/g) || []).length + " / 3"
rows.push(`${i.static}${anjou} ${reikar} ${lorren} ${lucile} ${weima} ${souma} ${vanisha} ${drolba} ${quibel} `);
};
await Promise.all(array.map(table));
document.querySelector("#mvContent").innerHTML = `
char rga lvl anjou reikar lorren lucile weima souma vanisha drolba quibel
${rows.join('')}
`
await sortableTables();
document.querySelector("#loadingDiv").remove();
};
async function mvCharacterCodex(rgas){
const endpoints = ['changefaction'];
const array = await mvFetch(rgas,endpoints);
const rows = [];
const table = async (i) => {
const completed = (i.changefaction.match(/Triworld Codex Chapter [0-9]+ /g) || ['0']).map(i => parseInt(i.match(/[0-9]+/i)))
const incomplete = [];
for (let i = 1; i <= 50; i++) {
if (!completed.includes(i)) { incomplete.push(i) };
};
rows.push(`${i.static}${completed.length} ${incomplete.join(',')} `);
};
await Promise.all(array.map(table));
document.querySelector("#mvContent").innerHTML = `
`
await filterTables();
await sortableTables();
document.querySelector("#loadingDiv").remove();
};
async function mvEquipmentEquipped(rgas,slot){
const endpoints = ['profile'];
const array = await mvFetch(rgas,endpoints);
const rows = [];
const table = async (i) => {
const slotsize = 'height:35px;width:35px'
const mr = i.profile.maxrage.toLocaleString();
const power = i.profile.power.toLocaleString();
const ele = i.profile.elemental.toLocaleString();
const core = ` `
const head = ` `
const neck = ` `
const weapon = ` `
const body = ` `
const shield = ` `
const belt = ` `
const pants = ` `
const ring = ` `
const foot = ` `
const chaosgem = ` `
const badge = ` `
const rune = ` `
const orb1 = ` `
const orb2 = ` `
const orb3 = ` `
rows.push(`${i.static}${power} ${ele} ${mr.toLocaleString()} ${core} ${head} ${neck} ${weapon} ${body} ${shield} ${belt} ${pants} ${ring} ${foot} ${chaosgem} ${badge} ${rune} ${orb1} ${orb2} ${orb3} `);
};
await Promise.all(array.map(table));
document.querySelector("#mvContent").innerHTML = `
char rga lvl POW ELE MR COR HED NCK WEP BDY SHD BELT PNT RNG FOT GEM BDG RNE ORB ORB ORB
${rows.join('').replace(/onclick="[^"]*"/g,'')}
`
await sortableTables();
document.querySelector("#loadingDiv").remove();
};
async function mvEquipmentSlot(rgas,slot){
const endpoints = ['profile'];
const array = await mvFetch(rgas,endpoints);
const rows = [];
const table = async (i) => {
const itemid = i.profile[slot].id;
const itemData = await superfetchItem(itemid);
rows.push(`
${i.static}
${itemData.name}
${itemData.augs}
${itemData.gems}
${itemData.atk.toLocaleString()}
${itemData.hp.toLocaleString()}
${itemData.ele.toLocaleString()}
${itemData.chaosdmg}
${itemData.resist}
${itemData.chaosres}
${itemData.vile}
${itemData.rpt.toLocaleString()}
${itemData.ept.toLocaleString()}
${itemData.rampage}%
${itemData.critical}%
${itemData.maxrage.toLocaleString()}
${itemData.block}%
${itemData.eleblock}%
${itemData.ps}
`);
};
await Promise.all(array.map(table));
document.querySelector("#mvContent").innerHTML = `
char rga lvl name img augs gem atk hp ele chs res res vle rpt ept rmp crt mr blk blk ps
${rows.join('')}
`
await sortableTables();
document.querySelector("#loadingDiv").remove();
};
async function mvEquipmentChaosGem(rgas){
const endpoints = ['profile'];
const array = await mvFetch(rgas,endpoints);
const rows = [];
const table = async (i) => {
const itemid = i.profile.gem.id;
const itemData = await superfetchItem(itemid);
rows.push(`
${i.static}
${itemData.name}
${itemData.chaosdmg.toLocaleString()}
${itemData.rampage}%
${itemData.critical}%
${itemData.maxrage.toLocaleString()}
`);
};
await Promise.all(array.map(table));
document.querySelector("#mvContent").innerHTML = `
char rga lvl name img chaos rampage critical max rage
${rows.join('')}
`
await sortableTables();
document.querySelector("#loadingDiv").remove();
};
async function mvEquipmentBadge(rgas){
const endpoints = ['profile'];
const array = await mvFetch(rgas,endpoints);
const rows = [];
const table = async (i) => {
const itemid = i.profile.badge.id;
const itemData = await superfetchItem(itemid);
rows.push(`
${i.static}
${itemData.name}
${itemData.atk.toLocaleString()}
${itemData.hp.toLocaleString()}
${itemData.ele.toLocaleString()}
`);
};
await Promise.all(array.map(table));
document.querySelector("#mvContent").innerHTML = `
char rga lvl name img atk hp elemental
${rows.join('')}
`
await sortableTables();
document.querySelector("#loadingDiv").remove();
};
async function mvEquipmentOrbs(rgas){
const endpoints = ['profile'];
const array = await mvFetch(rgas,endpoints);
const rows = [];
const table = async (i) => {
const orb1id = i.profile.orb1.id;
const orb2id = i.profile.orb2.id;
const orb3id = i.profile.orb3.id;
const orb1Data = await superfetchItem(orb1id);
const orb2Data = await superfetchItem(orb2id);
const orb3Data = await superfetchItem(orb3id);
rows.push(`
${i.static}
${orb1Data.name}
${orb2Data.name}
${orb3Data.name}
${(orb1Data.maxrage + orb2Data.maxrage + orb3Data.maxrage).toLocaleString()}
${(orb1Data.ele + orb2Data.ele + orb3Data.ele).toLocaleString()}
${(orb1Data.atk + orb2Data.atk + orb3Data.atk).toLocaleString()}
${(orb1Data.hp + orb2Data.hp + orb3Data.hp).toLocaleString()}
${(orb1Data.rpt + orb2Data.rpt + orb3Data.rpt).toLocaleString()}
${(orb1Data.ept + orb2Data.ept + orb3Data.ept).toLocaleString()}
${(orb1Data.chaosdmg + orb2Data.chaosdmg + orb3Data.chaosdmg).toLocaleString()}
`);
};
await Promise.all(array.map(table));
document.querySelector("#mvContent").innerHTML = `
char rga lvl img img img orb 1 orb 2 orb 3 mr ele atk hp rpt ept chaos
${rows.join('')}
`
await sortableTables();
document.querySelector("#loadingDiv").remove();
};
async function mvEquipmentRune(rgas){
const endpoints = ['profile','ajax/backpackcontents.php?tab=quest'];
const array = await mvFetch(rgas,endpoints);
const rows = [];
const table = async (i) => {
const fusers = (i.quest.match(/data-name="Elemental Fuser" data-itemqty="([0-9]+)"/i) || [0,0])[1];
const itemid = i.profile.rune.id;
const itemData = await superfetchItem(itemid);
rows.push(`
${i.static}
${itemData.name}
${itemData.ele.toLocaleString()}
${fusers}
`);
};
await Promise.all(array.map(table));
document.querySelector("#mvContent").innerHTML = `
char rga lvl name img elemental
${rows.join('')}
`
await sortableTables();
document.querySelector("#loadingDiv").remove();
};
async function mvEquipmentBooster(rgas){
const endpoints = ['profile'];
const array = await mvFetch(rgas,endpoints);
const rows = [];
const table = async (i) => {
const itemid = i.profile.booster.id;
const itemData = await superfetchItem(itemid);
rows.push(`
${i.static}
${itemData.name}
`);
};
await Promise.all(array.map(table));
document.querySelector("#mvContent").innerHTML = `
char rga lvl booster img
${rows.join('')}
`
await sortableTables();
document.querySelector("#loadingDiv").remove();
};
async function mvEquipmentCrests(rgas){
const endpoints = ['profile'];
const array = await mvFetch(rgas,endpoints);
const rows = [];
const table = async (i) => {
const crest1 = await superfetchItem(i.profile.ccrest.id);
const crest2 = await superfetchItem(i.profile.pcrest.id);
const crest3 = await superfetchItem(i.profile.fcrest.id);
const crest4 = await superfetchItem(i.profile.acrest.id);
rows.push(
`
${i.static}
${crest1.name}
${crest3.name}
${crest2.name}
${crest4.name}
`
);
};
await Promise.all(array.map(table));
document.querySelector("#mvContent").innerHTML = `
char rga lvl img img img img class ferocity preservation affliction
${rows.join('')}
`
await sortableTables();
document.querySelector("#loadingDiv").remove();
};
async function mvEquipmentAugments(rgas){
const endpoints = ['profile'];
const array = await mvFetch(rgas,endpoints);
const rows = [];
let totalOpen = 0;
const table = async (i) => {
const core = await superfetchItem(i.profile.core.id);
const head = await superfetchItem(i.profile.head.id);
const neck = await superfetchItem(i.profile.neck.id);
const weapon = await superfetchItem(i.profile.weapon.id);
const body = await superfetchItem(i.profile.body.id);
const shield = await superfetchItem(i.profile.shield.id);
const belt = await superfetchItem(i.profile.belt.id);
const pants = await superfetchItem(i.profile.pants.id);
const ring = await superfetchItem(i.profile.ring.id);
const foot = await superfetchItem(i.profile.foot.id);
const allaugs = core.augs + head.augs + neck.augs + weapon.augs + body.augs + shield.augs + belt.augs + pants.augs + ring.augs + foot.augs
const augcount = (allaugs.match(/itempopup\(event,'[0-9]+_[0-9]+'\)/g) || []).length
totalOpen += core.openaugs + head.openaugs + neck.openaugs + weapon.openaugs + body.openaugs + shield.openaugs + belt.openaugs + pants.openaugs + ring.openaugs + foot.openaugs
rows.push(`${i.static}${augcount} ${allaugs} `)
};
await Promise.all(array.map(table));
document.querySelector("#mvContent").innerHTML = `
TOTAL OPEN AUG SLOTS
${totalOpen}
char rga lvl count slotted augments
${rows.join('')}
`
await sortableTables();
document.querySelector("#loadingDiv").remove();
};
async function mvStorageBackpack(rgas){
const endpoints = ['ajax/backpackcontents.php?tab=regular'];
const array = await mvFetch(rgas,endpoints);
const rows = [];
const table = async (i) => {
const bpCapacity = parseInt(i.regular.match(/data-maxval="([0-9]+)"/i)[1]);
const bpCount = parseInt(i.regular.match(/data-curitemct="([0-9]+)"/i)[1]);
const bpOpen = bpCapacity-bpCount;
const match = i.regular.replace(/\s+/g, ' ').replace(/[\n\r]/g,'').match(/src="[^"]*" alt="[^"]*" ONMOUSEOVER="itempopup\(event,'[0-9]+'\)"/g) || [];
const items = match.length < 250 ? match.map(i => ` `) : ['Too many items to display'];
rows.push(`
${i.static}
${bpCount}
${bpCapacity}
${bpOpen}
${items.join('')}
`);
};
await Promise.all(array.map(table));
document.querySelector("#mvContent").innerHTML = `
char rga lvl count capacity open slots backpack items
${rows.join('')}
`
// Backpack item actions
const selectable = document.querySelectorAll('img.mv-selectable');
for (var i = 0; i < selectable.length; i++){
const item = selectable[i];
item.addEventListener('click', async function(){
item.classList.toggle('mv-backpack-selected')
})
}
// Add sortable
await sortableTables();
// Remove loading
document.querySelector("#loadingDiv").remove();
};
async function mvStorageItems(rgas,tab){
const endpoints = [`ajax/backpackcontents.php?tab=${tab}`];
const array = await mvFetch(rgas,endpoints);
const headerArray = [];
// Pull list of all available items and push to array
const itemsList = async (i) => {
const itemsParse = i[tab].replace(/\s+/g, ' ').replace(/[\n\r]/g,'').replace(/'/g,'').match(/src="[^"]*" alt="[^"]*"/g);
if (itemsParse){
for (let n = 0; n < itemsParse.length; n++) {
headerArray.push(itemsParse[n]);
};
};
};
await Promise.all(array.map(itemsList));
// Sort the list of available items alphabetically
headerArray.sort((a, b) => {
const altA = a.match(/alt="([^"]*)"/)[1];
const altB = b.match(/alt="([^"]*)"/)[1];
return altA.localeCompare(altB);
});
// Remove duplicates
const header = ([...new Set(headerArray)]).map(i => ` `);
// Capture the number of tables needed if each table contains 18 items
const noOfTables = Math.ceil(header.length/18);
// Build headers and rows array to populate for each page
const headers = []
const rows = []
// Loop through based on number of tables needed
for (let i = 0; i < noOfTables; i++) {
// Build header arrays of data
const slicedHeader = header.slice(i*18,(i+1)*18);
headers[i+1] = slicedHeader;
// Create row array to push each row
const row = []
// Loop through each character
const table = async (a) => {
// Parse the name of each item found
const names = slicedHeader.join('').match(/event,'[^']*'/g).map(p => p.match(/'([^']*)'/)[1]);
const item = a[tab].replace(/\s+/g, ' ').replace(/[\n\r]/g,'').replace(/'/g,'');
// Create cells variable with static data from array
let cells = a.static;
// Loop through each item and parse the quantity found
for (let n = 0; n < names.length; n++) {
const regex = new RegExp(`data-itemidqty="[0-9]+" data-name="${names[n]}"`);
const count = (item.match(regex) || ['itemidqty="0"']).map(m => m.match(/itemidqty="([0-9]+)"/)[1]);
// Add the table cell with the quantity value to cells variable
cells += '' + count + ' ';
};
// Push entire table row of static data and item counts to the row array
row.push('' + cells + ' ');
};
await Promise.all(array.map(table));
// Set the value of each table rows[i] to the final row array
rows[i+1] = row;
};
document.querySelector("#mvContent").innerHTML = `
char rga lvl ${headers[1].join('')}
${rows[1].join('')}
`
// Check if more than 1 table is needed to display all available items
if (noOfTables > 1){
const tableLinks = [];
// Add table links to the top of the container div for each table needed
for (let i = 0; i < noOfTables; i++) {
tableLinks.push(`${i+1} `)
};
document.querySelector("#tableNoSpan").innerHTML = `${tableLinks.join('')} `
// Add event listeners to view different tables as needed
for (let i = 0; i < noOfTables; i++) {
document.querySelector(`#items-table-${i+1}`).addEventListener('click', async function(){
document.querySelector("#mv-table-container").innerHTML = `
char rga lvl ${headers[i+1].join('')}
${rows[i+1].join('')}
`
await sortableTables();
});
};
};
await sortableTables();
document.querySelector("#loadingDiv").remove();
}
async function mvStorageEquipment(rgas){
const endpoints = ['augmentequip'];
const array = await mvFetch(rgas,endpoints);
const rows = [];
let tableMsg = ''
const table = async (i) => {
if (i.augmentequip.match(/Manage Augments<\/h1>/i)){;
const items = i.augmentequip.match(/ /g)
if (items){
rows.push('' + i.static + '' + items.join('') + ' ')[1]
};
} else {
tableMsg = 'Equipment will only display for the originating RGA because security word is required to access items '
};
};
await Promise.all(array.map(table));
document.querySelector("#mvContent").innerHTML = `
${tableMsg}
char rga lvl all equipment
${rows.join('')}
`
await sortableTables();
document.querySelector("#loadingDiv").remove();
}
async function mvStorageVault(rgas){
const endpoints = ['vault'];
const array = await mvFetch(rgas,endpoints);
let tableMsg = ''
const rows = [];
const table = async (i) => {
if (i.vault.match(/Currently Storing/i)){;
const vaultCapacity = parseInt(i.vault.match(/Currently Storing [0-9]+ \/ ([0-9]+)<\/b>/i)[1]);
const vaultCount = parseInt(i.vault.match(/Currently Storing ([0-9]+) \/ [0-9]+<\/b>/i)[1]);
const vaultOpen = vaultCapacity-vaultCount;
const match = i.vault.replace(/[\n\r]/g,'').replace(/.*images\/vault_inventory\.gif/i,'').match(/src="[^"]*" ONMOUSEOVER="itempopup\(event,'[0-9]+'\)"/g)
if (match){
const items = match.map(m => ` `);
rows.push(`
${i.static}
${vaultCount}
${vaultCapacity}
${vaultOpen}
${items.join('')}
`);
};
} else {
tableMsg = 'Vault items will only display for the originating RGA because security word is required to access items '
};
};
await Promise.all(array.map(table));
document.querySelector("#mvContent").innerHTML = `
${tableMsg}
char rga lvl items capacity open slots vault
${rows.join('')}
`
await sortableTables();
document.querySelector("#loadingDiv").remove();
};
async function mvStorageAugments(rgas){
const endpoints = ['augmentequip'];
const array = await mvFetch(rgas,endpoints);
const rows = [];
let tableMsg = ''
const table = async (i) => {
if (i.augmentequip.match(/Manage Augments<\/h1>/i)){;
const match = i.augmentequip.match(/width="20" height="20" src="[^"]*" onmouseover="itempopup\(event,'[0-9]+'\)"/g)
if (match){
const items = match.map(m => ` `);
rows.push('' + i.static + '' + items.join('') + ' ')[1]
};
} else {
tableMsg = 'Augments will only display for the originating RGA because security word is required to access items '
};
};
await Promise.all(array.map(table));
document.querySelector("#mvContent").innerHTML = `
${tableMsg}
char rga lvl all augments
${rows.join('')}
`
await sortableTables();
document.querySelector("#loadingDiv").remove();
};
// Moxxivision plus tabs
async function mvItemSlotting(rgas,string){
document.querySelector("#mvContent").innerHTML = `
`
// Text box input function
document.querySelector("#itemLink").addEventListener('input', async function() {
const input = document.querySelector("#itemLink").value;
const link = input.match(/itemlink\?id=[0-9]+/i);
// If item link
if (link){
// Get item id
const itemidNew = link.toString().match(/[0-9]+/);
// Parse stats of the item
const sNew = await superfetchItem(itemidNew);
const imgNew = sNew.img;
const nameNew = sNew.name;
const eleNew = sNew.ele;
const mrNew = sNew.maxrage;
const slot = sNew.slot;
// Fetch endpoints
if (!string.match('Full')){ rgas = rgas.slice(0, 2) };
const endpoints = ['profile','equipment?r=0'];
const array = await mvFetch(rgas,endpoints);
const rows = [];
// Fetch data
const table = async (i) => {
const mrChar = i.profile.maxrage;
const eleChar = i.profile.elemental;
const itemid = i.profile[slot].id;
const s = await superfetchItem(itemid);
const imgCurrent = s.img;
const nameCurrent = s.name;
const eleCurrent = s.ele;
const mrCurrent = s.maxrage;
rows.push(`
${i.static}
${eleChar.toLocaleString()}
${mrChar.toLocaleString()}
${nameCurrent}
${eleCurrent.toLocaleString()}
${mrCurrent.toLocaleString()}
${nameNew}
${eleNew.toLocaleString()}
${mrNew.toLocaleString()}
${(eleNew-eleCurrent).toLocaleString()}
${(mrNew-mrCurrent).toLocaleString()}
`);
};
await Promise.all(array.map(table));
document.querySelector("#item-slotting-div").innerHTML = `
character
rga
lvl
tot ele
tot mr
img
current item
ele
mr
img
new item
ele
mr
ele change
mr change
${rows.join('')}
`
await sortableTables();
if (!string.match('Full')){ await mvNotSpecial("ITEM SLOTTING") };
document.querySelector("#loadingDiv").remove();
} else {
alert('Invalid item link entered');
};
});
};
async function mvAugmentData(rgas,string){
if (!string.match('Full')){ rgas = rgas.slice(0, 2) };
const endpoints = ['profile'];
const array = await mvFetch(rgas,endpoints);
const rows = [];
const table = async (i) => {
const augObj = {};
let augTotal = 0;
let augTotalEle = 0;
let augTotalPower = 0;
let augTotalMr = 0;
let augTotalChaos = 0;
let augTotalVile = 0;
const core = await superfetchItem(i.profile.core.id);
const head = await superfetchItem(i.profile.head.id);
const neck = await superfetchItem(i.profile.neck.id);
const weapon = await superfetchItem(i.profile.weapon.id);
const body = await superfetchItem(i.profile.body.id);
const shield = await superfetchItem(i.profile.shield.id);
const belt = await superfetchItem(i.profile.belt.id);
const pants = await superfetchItem(i.profile.pants.id);
const ring = await superfetchItem(i.profile.ring.id);
const foot = await superfetchItem(i.profile.foot.id);
const augIdsArrayOfArrays = [core.augids,head.augids,neck.augids,weapon.augids,body.augids,shield.augids,belt.augids,pants.augids,ring.augids,foot.augids];
const allAugIds = augIdsArrayOfArrays.flat();
const processAugs = async (id) => {
const aug = await superfetchItem(id);
augTotal += 1;
augTotalEle += aug.ele;
augTotalPower += aug.atk;
augTotalPower += aug.hp;
augTotalMr += aug.maxrage;
augTotalChaos += aug.chaosdmg;
augTotalVile += aug.vile;
if (augObj[aug.name]){
augObj[aug.name].count += 1;
} else {
augObj[aug.name] = { id: id, name: aug.name, img: aug.img, count: 1 };
};
};
await Promise.all(allAugIds.map(processAugs));
// Sort aug object from highest to lowest and then convert back to an object
const sortedArrayOfAugs = Object.entries(augObj).sort(([, a], [, b]) => b.count - a.count);
const sortedObjectOfAugs = Object.fromEntries(sortedArrayOfAugs);
// Process augObj image and count data
const augImgArray = [];
Object.keys(sortedObjectOfAugs).forEach(type => {
const obj = augObj[type];
const img = obj.img;
const count = obj.count;
const id = obj.id;
augImgArray.push(`${count}
`)
});
// Build rows
rows.push(`
${i.static}
${augTotal}
${augTotalEle.toLocaleString()}
${augTotalPower.toLocaleString()}
${augTotalMr.toLocaleString()}
${augTotalChaos.toLocaleString()}
${augImgArray.join('')}
`);
};
await Promise.all(array.map(table));
document.querySelector("#mvContent").innerHTML = `
char rga lvl augs augs ele augs power augs max rage augs chaos quantities
${rows.join('')}
`
await sortableTables();
document.querySelector("#loadingDiv").remove();
};
async function mvMaxRage(rgas,string){
const endpoints = ['home','profile'];
if (!string.match('Full')){ rgas = rgas.slice(0, 2) };
const array = await mvFetch(rgas,endpoints);
const rows = [];
const table = async (i) => {
const mr = i.home.maxrage;
const charclass = i.profile.class;
// Get enhancement data
const upgraded = i.home.mrupgrades.purchased;
const max = i.home.mrupgrades.max;
const available = max - upgraded;
// Get equipment data
let mrFromItems = 0;
let mrFromGems = 0;
let missingGems = 40;
let costToGem = 0;
const core = await superfetchItem(i.profile.core.id);
const head = await superfetchItem(i.profile.head.id);
const neck = await superfetchItem(i.profile.neck.id);
const weapon = await superfetchItem(i.profile.weapon.id);
const body = await superfetchItem(i.profile.body.id);
const shield = await superfetchItem(i.profile.shield.id);
const belt = await superfetchItem(i.profile.belt.id);
const pants = await superfetchItem(i.profile.pants.id);
const ring = await superfetchItem(i.profile.ring.id);
const foot = await superfetchItem(i.profile.foot.id);
const eq = [core,head,neck,weapon,body,shield,belt,pants,ring,foot];
for (let n = 0; n < 10; n++) {
const item = eq[n];
const rarity = item.rarity;
const maxrage = item.maxrage;
mrFromItems += maxrage;
const gems = item.gems;
let x = gems == 3 ? 0.15 : gems == 2 ? 0.3225 : gems == 1 ? 0.520875 : gems == 0 ? 0.74900625 : 0;
if (charclass == "Monster") {
x *= 1.1
} else if (charclass == "Pop Star"){
x *= 1.05
};
mrFromGems += (maxrage * x);
missingGems -= gems;
if (rarity != ''){
const upgrades = await info("Cost to Full Gem");
costToGem += upgrades[rarity][gems]
};
}
// Calculate the potential max rage if all items are fully gemmed
const maxMr = Math.ceil(mrFromGems) + mr + available;
// Calculate the max rage gained per point spent if all items are fully gemmed
const perPoint = missingGems == 0 ? 0 : Math.ceil(mrFromGems/costToGem)
// Parse character max rage
rows.push(`
${i.static}
${charclass}
${mr.toLocaleString()}
${upgraded.toLocaleString()}
${available.toLocaleString()}
${missingGems}
${(Math.ceil(mrFromGems) + available).toLocaleString()}
${costToGem.toLocaleString()}
${maxMr.toLocaleString()}
${perPoint}
`);
};
await Promise.all(array.map(table));
document.querySelector("#mvContent").innerHTML = `
char
rga
lvl
class
max rage
char upgrades
upgrades available
missing gems
max gains
full gem cost
mr if maxed
mr per point
${rows.join('')}
`
await sortableTables();
if (!string.match('Full')){ await mvNotSpecial("MAX RAGE") };
document.querySelector("#loadingDiv").remove();
};
async function mvEvents(rgas,event,string){
const peram = event == "WAR OF ZHUL" ? "woz" : "top"
const qItemLong = event == "WAR OF ZHUL" ? "Summoning Shard" : "Trial Insignia"
const qItemShort = event == "WAR OF ZHUL" ? "shards" : "insignias"
const qItemImg = event == "WAR OF ZHUL" ? "images/warshard.jpg" : "images/items/trialinsignia.jpg"
// Get war of zhul event info and calculate hours left
let runsLeft;
const eventPage = await superfetch(`event?eventid=${peram}`);
if (eventPage.match('START COUNTDOWN')){
runsLeft = 31;
} else if (eventPage.match(/countdown = ([0-9]+)/i)){
const timer = parseInt(eventPage.match(/countdown = ([0-9]+)/i)[1])*1000;
const now = ((new Date().getTime()) - 18000000);
runsLeft = (timer-now)/3600000/10.8;
} else {
runsLeft = 31;
};
// Fetch endpoints
if (!string.match('Full')){ rgas = rgas.slice(0, 2) };
const endpoints = ['home','ajax/backpackcontents.php?tab=quest','ajax/backpackcontents.php?tab=potion'];
const array = await mvFetch(rgas,endpoints);
const rows = [];
const regex = new RegExp(`data-itemidqty="[0-9]+" data-name="${qItemLong}"`);
const table = async (i) => {
const fury = parseInt((i.potion.match(/data-itemidqty="([0-9]+)" data-name="Recharge the Fury"/i) || [0,0])[1]);
const spark = parseInt((i.potion.match(/data-itemidqty="([0-9]+)" data-name="Spark the Fury"/i) || [0,0])[1]);
const elepot = parseInt((i.potion.match(/data-itemidqty="([0-9]+)" data-name="Potion of Elemental Resistance"/i) || [0,0])[1]);
const kixpot = parseInt((i.potion.match(/data-itemidqty="([0-9]+)" data-name="Kix Potion"/i) || [0,0])[1]);
const amdirpot = parseInt((i.potion.match(/data-itemidqty="([0-9]+)" data-name="Potion of Amdir"/i) || [0,0])[1]);
const squidpot = parseInt((i.potion.match(/data-itemidqty="([0-9]+)" data-name="Squidberry Juice"/i) || [0,0])[1]);
const wonderlandpot = parseInt((i.potion.match(/data-itemidqty="([0-9]+)" data-name="Wonderland Potion"/i) || [0,0])[1]);
const refills = fury + (spark/2);
const qitem = parseInt((i.quest.match(regex) || '0').toString().match(/[0-9]+/i));
const mr = i.home.maxrage;
const rpt = i.home.rageperturn;
rows.push(`
${i.static}
${fury}
${spark}
${elepot}
${kixpot}
${amdirpot}
${squidpot}
${wonderlandpot}
${qitem}
${mr.toLocaleString()}
${rpt.toLocaleString()}
${Math.ceil(runsLeft*(mr+rpt)/20000+qitem)}
${Math.ceil(refills*mr/20000) + Math.ceil(runsLeft*(mr+rpt)/20000+qitem)}
`);
};
await Promise.all(array.map(table));
document.querySelector("#mvContent").innerHTML = `
char
rga
lvl
max rage
rpt
proj tot ${qItemShort}
furys to use
new proj ${qItemShort}
${rows.join('')}
`
// Updates the projected shards with fury when the input box value changes
const furyInput = Array.from(document.querySelectorAll(".fury-math-input"));
for (let i = 0; i < furyInput.length; i++) {
furyInput[i].addEventListener('input',async function(){
const cellInput = parseFloat(this.value);
const cellOutput = this.parentNode.parentNode.querySelector('.fury-math-output');
const cellMaxRage = parseInt(this.parentNode.parentNode.querySelector('.max-rage').innerHTML.replace(/,/g,''));
const cellQItem = parseInt(this.parentNode.parentNode.querySelector('.projected-qitem').innerHTML.replace(/,/g,''));
cellOutput.innerHTML = Math.ceil(((cellInput*cellMaxRage)/20000) + cellQItem);
});
};
await sortableTables();
if (!string.match('Full')){ await mvNotSpecial(event) };
document.querySelector("#loadingDiv").remove();
};
async function mvHalloween(rgas,string){
if (!string.match('Full')){ rgas = rgas.slice(0, 2) };
const endpoints = ['home','ajax/backpackcontents.php?tab=quest','quest_info.php?questnum=2498'];
const array = await mvFetch(rgas,endpoints);
const rows = [];
const table = async (i) => {
const mr = i.home.maxrage;
const rpt = i.home.rageperturn;
const components = parseInt((i.quest.match(/data-itemidqty="([0-9]+)" data-name="Server Components"/i) || [0,0])[1]);
let queststep;
if (i.questnum.match(/Server Components:<\/b> [0-9]+\/[0-9]+/i)){
const componentsNeeded = i.questnum.match(/Server Components:<\/b> [0-9]+\/([0-9]+)/i)[1];
queststep = `Find ${componentsNeeded} components`
} else if (i.questnum.match(/.*?: <\/b>[0-9]+\/[0-9]+ killed/i)){
const mobNeeded = i.questnum.match(/(.*?): <\/b>[0-9]+\/[0-9]+ killed/i)[1];
queststep = `Kill ${mobNeeded}`
} else if (i.questnum.match('error')){
queststep = 'Finished'
} else {
queststep = 'Unknown'
}
//const canes = parseInt((i.quest.match(/data-itemidqty="([0-9]+)" data-name="Candy Cane"/i) || [0,0])[1]);
//
rows.push(`
${i.static}
${mr.toLocaleString()}
${rpt.toLocaleString()}
${components}
${queststep}
`);
};
await Promise.all(array.map(table));
document.querySelector("#mvContent").innerHTML = `
char
rga
lvl
max rage
rpt
server components
quest step
${rows.join('')}
`
await sortableTables();
if (!string.match('Full')){ await mvNotSpecial("HOLIDAYS") };
document.querySelector("#loadingDiv").remove();
}
async function mvChristmas(rgas,string){
if (!string.match('Full')){ rgas = rgas.slice(0, 2) };
const endpoints = ['home','ajax/backpackcontents.php?tab=quest','quest_info.php?questnum=1193'];
const array = await mvFetch(rgas,endpoints);
const rows = [];
const table = async (i) => {
const mr = i.home.maxrage;
const rpt = i.home.rageperturn;
const components = parseInt((i.quest.match(/data-itemidqty="([0-9]+)" data-name="Candy Cane"/i) || [0,0])[1]);
let queststep;
if (i.questnum.match(/Candy Cane:<\/b> [0-9]+\/[0-9]+/i)){
const canesNeeded = i.questnum.match(/Candy Cane:<\/b> [0-9]+\/([0-9]+)/i)[1];
queststep = `Find ${canesNeeded} canes`
} else if (i.questnum.match('error')){
queststep = 'Finished'
} else {
queststep = 'Unknown'
}
//const canes = parseInt((i.quest.match(/data-itemidqty="([0-9]+)" data-name="Candy Cane"/i) || [0,0])[1]);
//
rows.push(`
${i.static}
${mr.toLocaleString()}
${rpt.toLocaleString()}
${components}
${queststep}
`);
};
await Promise.all(array.map(table));
document.querySelector("#mvContent").innerHTML = `
char
rga
lvl
max rage
rpt
candy canes
quest step
${rows.join('')}
`
await sortableTables();
if (!string.match('Full')){ await mvNotSpecial("HOLIDAYS") };
document.querySelector("#loadingDiv").remove();
}
// Moxxivision support functions
async function mvFetch(rgas,endpoints){
// Loading screen
const loadingDiv = document.createElement("div");
loadingDiv.innerHTML = `loading⧖ `
loadingDiv.id = "loadingDiv"
document.body.appendChild(loadingDiv);
// Data object to return
const array = [];
// Loop through endpoints
const fetch = async (endpoint) => {
for (let i = 0; i < rgas.length; i++) {
// Parse the endpoint key
const key = endpoint.match(/[a-zA-Z]{2,}(?![a-zA-Z])/g)?.pop();
// Parse the login string from each rgas element
const login = rgas[i][0]
// Convert with ? or & based on endpoint value
const url = endpoint.match(/\?/i) ? endpoint.replace(/\?/i,`?${login}&`) : `${endpoint}?${login}`
// Push the data to the object
let data;
if (url.match("profile")){
data = await superfetchProfile(url)
} else if (url.match("home")){
data = await superfetchHome(url);
} else {
data = await superfetch(url);
};
// Create a new char object if one doesn't exist already
if (!array[i]) { array[i] = {} };
// Build static data object (char name,rga name,level)
if (!array[i].static) {
array[i].static = `
${rgas[i][3]}
${rgas[i][1]}
${rgas[i][4]}
`
};
// Add the fetched data to the object for each endpoint key
array[i][key] = data;
};
};
await Promise.all(endpoints.map(fetch));
if (!document.querySelector("#mvCopyChars")){
await mvCopy();
};
return array;
};
async function mvCopy(){
var copyDiv = document.createElement('div');
copyDiv.innerHTML = ' '
copyDiv.style.position = 'fixed';
copyDiv.style.bottom = '15px';
copyDiv.style.left = '15px';
copyDiv.id = "mvCopyChars"
copyDiv.style.zIndex = '9999';
copyDiv.style.cursor = 'pointer';
document.body.appendChild(copyDiv);
// Onmouseover
document.querySelector("#mvCopyChars").setAttribute('onmouseover',`statspopup(event,'Copy list of selected chars to clipboard')`);
document.querySelector("#mvCopyChars").setAttribute('onmouseout','kill()');
// Copy chars button
document.querySelector("#mvCopyChars").addEventListener('click', async function(){
const checkboxes = document.querySelectorAll(".mv-checkbox");
const selectedCharsArray = [];
const loopThroughCheckboxes = async (i) => {
if(i.checked){
selectedCharsArray.push(i.outerHTML.match(/name="([^"]*)"/i)[1]);
};
};
await Promise.all(Array.from(checkboxes).map(loopThroughCheckboxes));
// Copy character list to clipboard
if (selectedCharsArray.length > 0){
navigator.clipboard.writeText(selectedCharsArray.join(','))
.then(function() {
alert(`Copied ${selectedCharsArray.length} names to clipboard: ${selectedCharsArray.join(',')}`)
}).catch(function(err) {
alert('Unable to copy: ', err);
});
} else {
alert('No characters selected');
};
});
};
async function mvItemSpec(itemid){
const item = (await superfetch(`item_rollover.php?id=${itemid}`)).replace(/ \(\+[0-9]+\)<\/span>/g,'').replace(//g,'').replace(/,|%/g,'');
const slot = (item.match(/\[Slot - (.*?)\] /i) || ['','slot unknown'])[1].toLowerCase();
const img = itemid > 0 ? ` /i) || ['',''])[1]}" onmouseover="itempopup(event,'${itemid}')" onmouseout="kill()"> ` : ' ';
const rarity = (item.match(/text-shadow: #47462E 1px 1px 2px;color:#([A-Za-z0-9]+)/i) || ['',''])[1];
const name = `` + (item.match(/align="left">(.*?)<\/td>/i) || ['',''])[1].replace('td colspan="2"','a')+' ';
const cloned = item.match(/\[Cloned: .*?\]/i) ? true : false;
const augs = '' + (item.match(/src="[^"]*" ONMOUSEOVER="itempopup\(event'[0-9]+_[0-9]+'\)"/g) || []).map(i => ` `).join('') + ' ';
const openaugs = item.match(/\/images\/augslot\.jpg/) ? item.match(/\/images\/augslot\.jpg/g).length : 0;
const gems = '' +(4-((item.match(/src="\/images\/gemslot2\.jpg"/g) || []).length)) + ' ';
const atk = '' + parseInt((item.match(/\+([0-9]+) ATK /i) || [0,0])[1]).toLocaleString() + ' ';
const hp = '' + parseInt((item.match(/\+([0-9]+) HP /i) || [0,0])[1]).toLocaleString() + ' ';
const ele1 = parseInt((item.match(/\+([0-9]+) Holy<\/span>/i) || [0,0])[1]);
const ele2 = parseInt((item.match(/\+([0-9]+) Arcane<\/span>/i) || [0,0])[1]);
const ele3 = parseInt((item.match(/\+([0-9]+) Shadow<\/span>/i) || [0,0])[1]);
const ele4 = parseInt((item.match(/\+([0-9]+) Fire<\/span>/i) || [0,0])[1]);
const ele5 = parseInt((item.match(/\+([0-9]+) Kinetic<\/span>/i) || [0,0])[1]);
const elemental = '' + (ele1 + ele2 + ele3 + ele4 + ele5).toLocaleString() + ' ';;
const ele6 = '' + parseInt((item.match(/\+([0-9]+) Chaos<\/span>/i) || [0,0])[1]).toLocaleString() + ' ';
const res1 = parseInt((item.match(/\+([0-9]+) Holy Resist/i) || [0,0])[1]);
const res2 = parseInt((item.match(/\+([0-9]+) Arcane Resist/i) || [0,0])[1]);
const res3 = parseInt((item.match(/\+([0-9]+) Shadow Resist/i) || [0,0])[1]);
const res4 = parseInt((item.match(/\+([0-9]+) Fire Resist/i) || [0,0])[1]);
const res5 = parseInt((item.match(/\+([0-9]+) Kinetic Resist/i) || [0,0])[1]);
const resist = '' + (res1 + res2 + res3 + res4 + res5).toLocaleString() + ' ';;
const res6 = '' + (item.match(/\+([0-9]+) Chaos Resist/i) || [0,0])[1] + ' ';
const vile = '' + parseInt((item.match(/\+([0-9]+) vile energy/i) || [0,0])[1]).toLocaleString() + ' ';
const rpt = '' + parseInt((item.match(/\+([0-9]+) rage per hr/i) || [0,0])[1]).toLocaleString() + ' ';
const ept = '' + parseInt((item.match(/\+([0-9]+) exp per hr/i) || [0,0])[1]).toLocaleString() + ' ';
const rampage = '' + (item.match(/\+([0-9]+) rampage/i) || [0,0])[1] + ' ';
const critical = '' + (item.match(/\+([0-9]+) critical hit/i) || [0,0])[1] + ' ';
const mr = '' + parseInt((item.match(/\+([0-9]+) max rage/i) || [0,0])[1]).toLocaleString() + ' ';
const block = '' + (item.match(/\+([0-9]+) block/i) || [0,0])[1] + ' ';
const eleblock = '' + (item.match(/\+([0-9]+) elemental block/i) || [0,0])[1] + ' ';
const ps = '' + (item.match(/\+(\d+(\.\d+)?) perfect strike/i) || [0,0])[1] + ' ';;
return {slot:slot,img:img,name:name,rarity:rarity,cloned:cloned,augs:augs,openaugs:openaugs,gems:gems,atk:atk,hp:hp,elemental:elemental,ele6:ele6,resist:resist,res6:res6,vile:vile,rpt:rpt,ept:ept,rampage:rampage,critical:critical,mr:mr,block:block,eleblock:eleblock,ps:ps};
};
async function mvNotSpecial(tab){
document.querySelector("#banner").innerHTML = `
`
};
// Add missing potions info to potions backpack
async function openPotionBp(){
window.XMLHttpRequest.prototype.realOpen = window.XMLHttpRequest.prototype.open;
window.XMLHttpRequest.prototype.open = function(method, url) {
if (url.includes('ajax/backpackcontents.php?tab=potion')) {
this.onreadystatechange = function() {
if (this.readyState === 4) {
var pbp = this.responseText;
const inbp = pbp.replace(/'/g,'').match(/data-name="[^"]*"/g).map(pot => pot.match(/"([^"]*)"/i)[1]);
info("All Potions").then(array => {
const allpots = array.map(([item]) => item);
const missing = allpots.filter(item => !inbp.includes(item) && item != "Boost One" && item != "Boost Two" && item != "Boost Three" && item != "Boost Four" && item != "Boost Five");
if (GM_getValue('auth').match('Full')){
document.querySelector("#backpackitemct").innerHTML = `missing `
} else {
document.querySelector("#backpackitemct").innerHTML = `missing `
};
});
};
};
};
this.realOpen.apply(this, arguments);
};
};
// Blank overlay to full-screen any Moxximod tools
async function blankOverlay(server,serverNo,rgaName,charId){
// Remove the on/off moxxi toggle in bottom-left
if (document.querySelector("#mmOnOff")){
document.querySelector("#mmOnOff").remove();
};
// Close apps menu
appsMenuClose();
// Append body with new div element
$("body").append(`
`);
// Disable main page scrolling
document.body.style.overflow = 'hidden';
// Get body background color and make the blankOverlay background the same color
document.querySelector("#blankOverlay").setAttribute('style',`background:#000000;padding:20px;`)
};
// Item On Hover Functions
async function itemOnHover(){
const tip = document.getElementById('dhtmltooltip');
// Mutation observer for dhtmltooltip element
const observerCallback = async () => {
if (tip.innerHTML.match(/id="itemtable"/i)) {
// Turn off the observer so the updated info doesn't create infinite loop
observer.disconnect();
// Parse the data
await parse(tip);
// Re-run function to re-apply observer
await itemOnHover();
};
};
const observer = new MutationObserver(observerCallback);
observer.observe(tip, { attributes: true, childList: true, subtree: true });
// Parse data function
async function parse(tip){
// Codex checker
if (tip.innerHTML.match(/Triworld Codex Chapter [0-9]+/i)){
const codex = tip.innerHTML.match(/(Triworld Codex Chapter [0-9]+)/i)[1];
tip.innerHTML = tip.innerHTML.replace('[Slot - Other]',' ')
const changefaction = await superfetch(`changefaction`);
const codexCheckDiv = document.querySelector("#codexCheck");
if (changefaction.match(codex) && codexCheckDiv){
document.querySelector("#codexCheck").innerHTML = `${codex} has already been activated on this account `
} else if (codexCheckDiv) {
document.querySelector("#codexCheck").innerHTML = `${codex} has not been activated on this account `
}
return;
};
// Parse ele damages
let itemHoly = parseInt((tip.innerHTML.replace(/,/g,'').match(/\+([0-9]+) Holy/i) || [0,0])[1]);
let itemArcane = parseInt((tip.innerHTML.replace(/,/g,'').match(/\+([0-9]+) Arcane/i) || [0,0])[1]);
let itemShadow = parseInt((tip.innerHTML.replace(/,/g,'').match(/\+([0-9]+) Shadow/i) || [0,0])[1]);
let itemFire = parseInt((tip.innerHTML.replace(/,/g,'').match(/\+([0-9]+) Fire/i) || [0,0])[1]);
let itemKinetic = parseInt((tip.innerHTML.replace(/,/g,'').match(/\+([0-9]+) Kinetic/i) || [0,0])[1]);
let itemChaos = parseInt((tip.innerHTML.replace(/,/g,'').match(/\+([0-9]+) Chaos/i) || [0,0])[1]);
let itemHolyRes = parseInt((tip.innerHTML.replace(/,/g,'').match(/\+([0-9]+) Holy Resist/i) || [0,0])[1]);
let itemArcaneRes = parseInt((tip.innerHTML.replace(/,/g,'').match(/\+([0-9]+) Arcane Resist/i) || [0,0])[1]);
let itemShadowRes = parseInt((tip.innerHTML.replace(/,/g,'').match(/\+([0-9]+) Shadow Resist/i) || [0,0])[1]);
let itemFireRes = parseInt((tip.innerHTML.replace(/,/g,'').match(/\+([0-9]+) Fire Resist/i) || [0,0])[1]);
let itemKineticRes = parseInt((tip.innerHTML.replace(/,/g,'').match(/\+([0-9]+) Kinetic Resist/i) || [0,0])[1]);
let augsHoly = 0
let augsArcane = 0
let augsShadow = 0
let augsFire = 0
let augsKinetic = 0
let augsChaos = 0
let augsHolyRes = 0
let augsArcaneRes = 0
let augsShadowRes = 0
let augsFireRes = 0
let augsKineticRes = 0
const allAugs = tip.innerHTML.match(/[0-9]+_[0-9]+/g) || []
// Loop through all augs
const augs = async (iid) => {
const aug = await superfetch(`item_rollover.php?id=${iid}`);
// Holy
itemHoly -= parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) Holy/i) || [0,0])[1]);
augsHoly += parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) Holy/i) || [0,0])[1]);
itemHolyRes -= parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) Holy Resist/i) || [0,0])[1]);
augsHolyRes += parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) Holy Resist/i) || [0,0])[1]);
// Arcane
itemArcane -= parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) Arcane/i) || [0,0])[1]);
augsArcane += parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) Arcane/i) || [0,0])[1]);
itemArcaneRes -= parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) Arcane Resist/i) || [0,0])[1]);
augsArcaneRes += parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) Arcane Resist/i) || [0,0])[1]);
// Shadow
itemShadow -= parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) Shadow/i) || [0,0])[1]);
augsShadow += parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) Shadow/i) || [0,0])[1]);
itemShadowRes -= parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) Shadow Resist/i) || [0,0])[1]);
augsShadowRes += parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) Shadow Resist/i) || [0,0])[1]);
// Fire
itemFire -= parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) Fire/i) || [0,0])[1]);
augsFire += parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) Fire/i) || [0,0])[1]);
itemFireRes -= parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) Fire Resist/i) || [0,0])[1]);
augsFireRes += parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) Fire Resist/i) || [0,0])[1]);
// Kinetic
itemKinetic -= parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) Kinetic/i) || [0,0])[1]);
augsKinetic += parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) Kinetic/i) || [0,0])[1]);
itemKineticRes -= parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) Kinetic Resist/i) || [0,0])[1]);
augsKineticRes += parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) Kinetic Resist/i) || [0,0])[1]);
// Chaos
itemChaos -= parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) Chaos/i) || [0,0])[1]);
augsChaos += parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) Chaos/i) || [0,0])[1]);
};
await Promise.all(allAugs.map(augs));
if (augsHoly > 0){ tip.innerHTML = tip.innerHTML.replace(/ \+(.*?) Holy/i,` +${itemHoly.toLocaleString()} (+${augsHoly}) Holy`) }
if (augsHolyRes > 0){ tip.innerHTML = tip.innerHTML.replace(/ \+(.*?) Holy Resist/i,` +${itemHolyRes.toLocaleString()} (+${augsHolyRes}) Holy Resist`) }
if (augsArcane > 0){ tip.innerHTML = tip.innerHTML.replace(/ \+(.*?) Arcane/i,` +${itemArcane.toLocaleString()} (+${augsArcane}) Arcane`) }
if (augsArcaneRes > 0){ tip.innerHTML = tip.innerHTML.replace(/ \+(.*?) Arcane Resist/i,` +${itemArcaneRes.toLocaleString()} (+${augsArcaneRes}) Arcane Resist`) }
if (augsShadow > 0){ tip.innerHTML = tip.innerHTML.replace(/ \+(.*?) Shadow/i,` +${itemShadow.toLocaleString()} (+${augsShadow}) Shadow`) }
if (augsShadowRes > 0){ tip.innerHTML = tip.innerHTML.replace(/ \+(.*?) Shadow Resist/i,` +${itemShadowRes.toLocaleString()} (+${augsShadowRes}) Shadow Resist`) }
if (augsFire > 0){ tip.innerHTML = tip.innerHTML.replace(/ \+(.*?) Fire/i,` +${itemFire.toLocaleString()} (+${augsFire}) Fire`) }
if (augsFireRes > 0){ tip.innerHTML = tip.innerHTML.replace(/ \+(.*?) Fire Resist/i,` +${itemFireRes.toLocaleString()} (+${augsFireRes}) Fire Resist`) }
if (augsKinetic > 0){ tip.innerHTML = tip.innerHTML.replace(/ \+(.*?) Kinetic/i,` +${itemKinetic.toLocaleString()} (+${augsKinetic}) Kinetic`) }
if (augsKineticRes > 0){ tip.innerHTML = tip.innerHTML.replace(/ \+(.*?) Kinetic Resist/i,` +${itemKineticRes.toLocaleString()} (+${augsKineticRes}) Kinetic Resist`) }
if (augsChaos > 0){ tip.innerHTML = tip.innerHTML.replace(/ \+(.*?) Chaos/i,` +${itemChaos.toLocaleString()} (+${augsChaos}) Chaos`) }
};
};
// Item Drop Menus
async function itemDropMenu(server,serverNo,rgaName){
const menu = document.getElementById('dropmenudiv');
// Mutation observer for dhtmltooltip element
const observerCallback = async () => {
// Turn off the observer so the updated info doesn't create infinite loop
observer.disconnect();
// Pull item information
const tip = document.getElementById('dhtmltooltip');
// Add codex RGA checker to menu
if (tip.innerHTML.match(/Triworld Codex Chapter [0-9]+/i)){
await codexMenuModify(menu,tip);
};
// Add augment menu
if (tip.innerHTML.match('Augment') && !tip.innerHTML.match('Add Augment Slot') && !tip.innerHTML.match('Remove All Augments') && !tip.innerHTML.match('Remove Augment')){
await augmentMenuModify(menu,tip);
}
if (tip.innerHTML.match('Badge of Absolution')){
await absolutionMenuModify(menu,tip);
}
// Re-run function to re-apply observer
await itemDropMenu(server,serverNo,rgaName);
};
const observer = new MutationObserver(observerCallback);
observer.observe(menu, { attributes: true, childList: true, subtree: true });
// Codex RGA checker
async function codexMenuModify(menu,tip){
const charId = (document.body.innerHTML.match(/outwar\.com\/page\?x=([0-9]+)/i) || [0,0])[1]
const codex = tip.innerHTML.match(/(Triworld Codex Chapter [0-9]+)/i)[1];
const newLink = document.createElement('a');
newLink.href = "javascript:void(0);";
newLink.onclick = async function() {
GM_addStyle(`#overlayWidget{text-align:center;}`)
await blankOverlay(server,serverNo,rgaName,charId);
document.querySelector("#overlayWidget").innerHTML = ' '
const rgaCharIds = document.body.innerHTML.replace(/[\n\r]/g,'').match(/.*?<\/optgroup>/i).toString().match(/value="[0-9]+"/g).map(i => i.match(/"([0-9]+)"/i)[1])
const data = [];
const rgaCodexLoop = async (id) => {
const changefaction = await superfetch(`changefaction?suid=${id}`);
const charName = (changefaction.match(/uname=(.*?)'/i) || ['','error'])[1];
const codexLvl = (changefaction.match(/Triworld Codex Chapter [0-9]+/g) || []).length;
const complete = changefaction.match(codex) ? "true" : "false";
if (charName != "error" && id != "0"){
const profileData = await superfetchProfile(`profile?id=${id}`);
const power = profileData.power;
const ele = profileData.elemental;
const loyalty = profileData.loyalty;
data.push(`${charName} ${loyalty} ${power.toLocaleString()} ${ele.toLocaleString()} ${complete} ${codexLvl} trade `);
};
};
await Promise.all(rgaCharIds.map(rgaCodexLoop));
document.querySelector("#overlayWidget").innerHTML = `
${codex}
character loyalty power ele completed? codex lvl trade
${data.join('')}
`
await sortableTables();
};
newLink.innerHTML = ` Codex Check`;
menu.appendChild(newLink);
};
async function augmentMenuModify(menu,tip){
const newLink = document.createElement('a');
newLink.href = "augmentequip";
newLink.innerHTML = ` Add Augment`;
menu.appendChild(newLink);
};
async function absolutionMenuModify(menu,tip){
const newLink = document.createElement('a');
newLink.href = "itemtransfer?type=selectbadge";
newLink.innerHTML = ` Transfer`;
menu.appendChild(newLink);
}
}
async function blankOff(){
GM_addStyle(`
@keyframes fadeOut {from {opacity: 1;} to {opacity: 0;}}
#blankOverlay {animation: fadeOut 1s ease;}
`)
await new Promise(resolve => setTimeout(resolve, 1000));
document.querySelector("#blankOverlay").remove();
document.body.style.overflowY = 'visible';
};
// Loading overlay to full-screen any Moxximod tools
async function loadingOverlay(){
GM_addStyle(`
#loadingOverlayText{text-align:center;background-color:#000000;color:#ffffff;font-size:24px;}
`)
// Append body with new div element
$("body").append(`
`);
// Disable main page scrolling
document.body.style.overflow = 'hidden';
// Get body background color and make the loadingOverlay background the same color
document.querySelector("#loadingOverlay").setAttribute('style',`background:#000000;padding:20px;`)
};
async function loadingOff(){
document.body.style.overflowY = 'visible';
document.querySelector("#loadingOverlay").remove();
};
// Fixed variable inf
async function info(request){
// List of all god slayer gods
if (request == 'Array of all gods'){
return [
"Ebliss Fallen Angel of Despair",
"Brutalitar Lord of the Underworld",
"Dreg nor Keeper of the Infernal Essence",
"King Ashnar Lord of the Unliving",
"Nar Zhul Slayer of All",
"Great Lord Ganeshan",
"Lady Ariella",
"Lord Narada",
"Lord Suka",
"Lord Varan",
"Synge The Red Dragon",
"Rancid Lord of Thugs",
"Terrance Rebel of Rallis",
"Zertan The Collector",
"Quiver The Renegade",
"Garland The Lord Keeper",
"Tylos The Lord Master",
"Jazzmin Maiden of Vitality",
"Sigil Lich of Woe",
"Ganja the Stone Golem",
"Lord Sibannac",
"Smoot the Yeti",
"Bloodchill the Grizzly",
"Ag Nabak the Abomination",
"Wanhiroeaz the Devourer",
"Vitkros Hydra of the Deep",
"Hyrak Bringer of Nightmares",
"Mistress of the Sword",
"Traxodon the Plaguebringer",
"Kro Shuk Doomslayer",
"Murderface",
"The Emerald Assassin",
"Detox",
"Samatha Dark-Soul",
"Anguish",
"Threk King of Lords",
"Crane",
"Gnorb",
"Nessam",
"Pinosis",
"Shadow",
"Tsort",
"Lord Xordam",
"Skybrine The Inescapable",
"Windstrike The Vile",
"Emperor Neudeus Controller of the Universe",
"Slashbrood Devourer of the Blackness",
"Howldroid Tormentor of the Pit",
"Hackerphage Protector of the Gateway",
"Numerocure The Black Messenger of Evil",
"Lady Chaos Queen of the Abyss",
"Rotborn Eater of the Dead",
"Melt Bane The Forbidden Demon Dragon",
"Baron Mu Dark Rider of the Undead",
"Freezebreed The Frozen Manipulator",
"Sylvanna TorLai",
"Lacuste of the Swarm",
"Anvilfist",
"Gorganus of the Wood",
"Ormsul the Putrid",
"Old World Drake",
"Animated Captain",
"Beast of Cards",
"Noxious Slug",
"Q-SEC Commander",
"Jade Dragonite",
"Varsanor Master of Darkness",
"Grivvek Protector of the Brood",
"Crantos Defender of Ultimation",
"Kretok Descendant of Nature",
"Felroc Overseer of Hellfire",
"Karvaz Lord of Alsayic",
"Sarcrina the Astral Priestess",
"Ancient Magus Tarkin",
"Jorun the Blazing Swordsman",
"Volgan the Living Ironbark",
"Zikkir the Dark Archer",
"Amalgamated Apparition",
"Nayark the Mummified Sorcerer",
"Akkel the Enflamed Warrior",
"Keeper of Nature",
"Archdevil Yirkon",
"Bolkor the Holy Master",
"Xynak the Arcane Master",
"Crolvak the Fire Master",
"Esquin the Kinetic Master",
"Raiyar the Shadow Master",
"Nafir God of Desolation",
"Skarthul the Avenged",
"Straya the Underworld Ruler",
"Dlanod the Crazed Chancellor",
"Viserion the Necrodragon",
"Balerion Dragon of Dread",
"Dexor Victor of Veldara",
"Gregov Knight of the Woods",
"Murfax Beast of the Caves",
"Thanox Balancer of Chaos",
"Rillax Twin of Wisdom",
"Villax Twin of Strength",
"Holgor the Holy Deity",
"Arcon the Arcane Deity",
"Firan the Fire Deity",
"Kinark the Kinetic Deity",
"Shayar the Shadow Deity",
"Agnar Astral Betrayer",
"Valzek Harbinger of Death",
"Envar Demon of Lunacy",
"Banok Demon of Insanity",
"Rezun Demon of Madness",
"Animation of Versatility",
"Animation of Elements",
"Animation of Power",
"Animation of Chaos"
];
// Cosmos info
} else if (request == 'Cosmos, Great All Being'){
return [100000000000,50,'Demonic Teleporter Recharge the Fury Cosmos Talisman Tome of Daily Grind Key to Knights Horror Astral Shard Quest Shard Recharge Totem Star Power Ticket to the Mystifying Carnival Containment Orb Orb of the Scepter Amulet Chest (50)']
// Death info
} else if (request == 'Death, Reaper of Souls'){
return [290000000000,80,'Recharge Totem Recharge the Fury Standard Issue Neuralyzer Death Talisman Pirate Treasure Map Key of the Elements Advanced Neuralyzer Trinket Items Elemental Vigor Orb Elemental Assault Orb Elemental Defense Orb Amulet Chest (50) Chancellor Items Spiral Gear']
// Maekrix info
} else if (request == 'Maekrix, Dreaded Striker'){
return [320000000000,73,'Red Dragon Items Astral Totem Maekrix Talisman Key to the Alsayic Ruins (Solo) Juggernaut Talisman Advanced Neuralyzer Irthys Vigor Orb Irthys Assault Orb Irthys Defense Orb Add Augment Slot Remove Augment Amulet Chest (50) Nobel Gear']
// Blackhand info
} else if (request == 'Blackhand Reborn'){
return [570000000000,61,'Augment of the Reborn Knight Core of Blackhand Essence of Reincarnation Blackhand Talisman Profound Ward 8-Bit Banana Buckler of Insanity Hauberk of Lunacy Charm of Havoc Unstoppable Concoction Advanced Neuralyzer Power Potion Pack Flask of Endurance Magic Gem Thunder Ball Perfection Gear Exalted Gear']
// Zyrak info
} else if (request == 'Zyrak, Vision of Madness'){
return [1200000000000,65,'Augment of Madness Unstable Jewel Veldarabloom Scripture of Zyrak Pulsating Stone Bottled Chaos Thunder Ball Force of Veldara Interstellar Vessel Vault Tear Vial of Insanity Demonic Madness Infinite Tower Spheroid Transcended Extract Tier 2 Booster Upgrade Ghostly Gear Boon of Vision Ancestral Tombs']
// Simulation info
} else if (request == 'Triworld Simulation'){
return [2400000000000,39,'20x Ask The Oracle Bundle 20x Remove Augment Bundle 5x Echo Trove Augment of Simulation Cosmic Mote Amalgamation Blossoms Catalysts of Accumulation Faction Change Codex Chapter 30 Codex Chapter 31 Triworld Experience Ward Descendant Set Items Quest Experience Potion Blazing Serpent Gear']
// Arkron info
} else if (request == 'Arkron, God of Trials'){
return [1500000000000,160,'']
// Gorath info
} else if (request == 'Gorath, Baron of Necromancy'){
return [1500000000000,160,'']
// Bazak info
} else if (request == 'Bazak, Demon of Hatred'){
return [1500000000000,160,'']
// Tarak info
} else if (request == 'Tarak, Brute of Affliction'){
return [1500000000000,160,'']
// Vorox info
} else if (request == 'Vorox, Mind of Ruin'){
return [1500000000000,160,'']
} else if (request == 'All Potions'){
return [
[`Triworld Tincture`,`/images/items/triworldtincture.png`],
[`Seething Echoes`,`/images/items/coepotion1.png`],
[`20 Year Aged Whiskey`,`/images/items/whiskeypot.png`],
[`Blazing Holiday Sauce`,`/images/items/emblemPotion2.jpg`],
[`Bottle of Holy Slaughter`,`/images/potion26.jpg`],
[`Brew of Precision`,`/images/items/potion3.gif`],
[`Holy Vile`,`/images/items/Pot_HolyVile.jpg`],
[`Damned Element Shot`,`/images/items/h16_Pot6.png`],
[`Demonic Madness`,`/images/items/vaultpot2.png`],
[`Dose of Destruction`,`/images/pot2.jpg`],
[`Evil Scream`,`/images/potion25.jpg`],
[`Fire Water`,`/images/potion22.jpg`],
[`Flask of Burning Souls`,`/images/items/basicflask1.gif`],
[`Flask of Conjured Lightning`,`/images/basicflask4.gif`],
[`Flask of Endurance`,`/images/items/itemz28.jpg`],
[`Flask of Flaming Death`,`/images/basicflask2.gif`],
[`Flask of Forbidden Knowledge`,`/images/basicflask3.gif`],
[`Flask of Super Nova`,`/images/basicflask5.gif`],
[`Funny Little Mushroom`,`/images/mushroom.jpg`],
[`Griznix Potion`,`/images/items/purepwnagepotion.png`],
[`Halloween Potion`,`/images/items/itemz27.jpg`],
[`Jabberwocky Blood`,`/images/items/Item_JabberwockyBlood.jpg`],
[`Kinetic Potency`,`/images/items/KineticShot.jpg`],
[`Kix Potion`,`/images/potion28.jpg`],
[`Kombucha`,`/images/items/Putrid%20Power%20Clusters.jpg`],
[`Liquid Bone Juice`,`/images/items/Item_JabberwockyBlood.jpg`],
[`Major Chaos Philter`,`/images/items/itemz82.jpg`],
[`Marsh Water`,`/images/potion24.jpg`],
[`Minor Chaos Philter`,`/images/items/itemz91.jpg`],
[`Olympian Juicebox`,`/images/items/lesserolympian.png`],
[`Olympian Push`,`/images/items/2k8.png`],
[`Potion of Amdir`,`/images/items/arelepot.jpg`],
[`Potion of Deceit`,`/images/items/potion2.gif`],
[`Potion of Elemental Resistance`,`/images/items/eleresistpotion.png`],
[`Potion of Enraged Alsayic`,`/images/items/PotionofEA.jpg`],
[`Pumpkin Juice`,`/images/halloween/PumpkinJuice.gif`],
[`Quantum Quattro`,`/images/items/pot_quantumquattro.jpg`],
[`Rampage Vile`,`/images/items/Pot_RampageVile.jpg`],
[`Reikavons Elixir`,`/images/items/ReikavonsElixer.jpg`],
[`Remnant Solice Lev 10`,`/images/items/90remnant.png`],
[`Remnant Solice Lev 11`,`/images/items/95remnant.png`],
[`Sammy Sosas Special Sauce`,`/images/pot5.jpg`],
[`Squidberry Juice`,`/images/items/Item_SquidberryJuice.jpg`],
[`Star Power`,`/images/items/starpowerelec.jpg`],
[`Strong Man Elixir`,`/images/items/potion1.gif`],
[`Sugar Daddy`,`/images/items/sugardaddy.png`],
[`Unstoppable Concoction`,`/images/items/juggerelepot.jpg`],
[`Vial of Insanity`,`/images/items/vaultpot1.png`],
[`Vile Energy Lev 6`,`/images/items/vile_energy_potion.jpg`],
[`Wonderland Potion`,`/images/items/itemz95.png`],
[`Zhulian Potion`,`/images/items/wozpotionzor.jpg`],
[`Zombie Potion 1`,`/images/items/potion_1.gif`],
[`Zombie Potion 2`,`/images/items/potion_2.gif`],
[`Zombie Potion 3`,`/images/items/potion_3.gif`],
[`Zombie Potion 4`,`/images/items/potion_4.gif`],
[`Zombie Potion 5`,`/images/items/potion_5.gif`],
[`Zombie Potion 6`,`/images/items/potion_6.gif`],
[`Boost One`,`/images/items/icon_vial_blue.jpg`],
[`Boost Two`,`/images/items/icon_vial_green.jpg`],
[`Boost Three`,`/images/items/icon_vial_orange.jpg`],
[`Boost Four`,`/images/items/icon_vial_red.jpg`],
[`Boost Five`,`/images/items/icon_vial_yellow.jpg`]
]
} else if (request = "Cost to Full Gem"){
return {
"cccccc":[10,9,7,4,0],
"FFFFFF":[25,23,18,10,0],
"1eff00":[50,45,35,20,0],
"ffde5b":[100,90,70,40,0],
"CA1111":[200,180,140,80,0],
"0070ff":[300,270,210,120,0],
"ff8000":[400,360,280,160,0],
"9000ba":[500,450,350,200,0]
};
} else if (request = "Cost to One Gem"){
return {
"cccccc":[1,2,3,4,0],
"FFFFFF":[2,5,8,10,0],
"1eff00":[5,10,15,20,0],
"ffde5b":[10,20,30,40,0],
"CA1111":[20,40,60,80,0],
"0070ff":[30,60,90,120,0],
"ff8000":[40,80,120,160,0],
"9000ba":[50,100,150,200,0]
}
} else {
return 'error';
};
};
// Superfetch
async function superfetch(url,skipCache){
if (!skipCache && superfetchCache.hasOwnProperty(url)) {
return superfetchCache[url];
} else {
while (true) {
try {
let data = await fetch(url).then(res => res.text());
if (data.match(/"error":"Rate limit exceeded/i)){
await new Promise(resolve => setTimeout(resolve, 1000));
} else {
// Cache the data from the URL fetch
superfetchCache[url] = data;
// Check if data contains an error and update to = "error" if it does
if (data.match('images/ErrorImg.jpg')){
data = "error"
};
// Return the data value
//console.log(`Superfetch complete for ${url}`)
return data;
};
} catch (error) {
await new Promise(resolve => setTimeout(resolve, 1000));
};
};
};
};
// Superpost
async function superpost(url,body){
while (true) {
try {
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: body
});
const data = await response.text();
if (data.includes("Rate limit exceeded. Please be kind to our servers ;)")) {
await new Promise(resolve => setTimeout(resolve, 1000));
} else {
return data;
}
} catch (error) {
await new Promise(resolve => setTimeout(resolve, 1000));
};
};
};
// Automatically update stored session ID when the session ID on Outwar changes
async function updateRga(rgaName){
const storedRga = GM_getValue("rgaName") || "none";
if (rgaName != storedRga){
await mmplus(`AuthCheck|${rgaName}`);
await GM_setValue("rgaName",rgaName);
};
};
// Function to send all server requests for Moxximod+
function mmplus(string) {
return new Promise((resolve) => {
if (document.querySelector("#rg_sess_id")) {
const rgaName = document.querySelector("#rg_sess_id").value;
auth(rgaName);
secword();
} else if (document.body.innerHTML.match(/rg_sess_id=([A-Za-z0-9]+)">[\n\r] /i)) {
const rgaName = document.body.innerHTML.match(/rg_sess_id=([A-Za-z0-9]+)">[\n\r] /i)[1];
auth(rgaName);
secword();
} else {
return;
};
function auth(rgaName){
const task = string.replace(/rganame/g,rgaName);
GM_xmlhttpRequest({
method: 'POST',
url: 'http://new.outwar.link:8001/',
data: task,
headers: {'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'},
onload: function(data) {
const response = data.response;
if (response.match(/^[a-zA-Z]+$/)){
GM_setValue("auth", response);
};
displayAuthStatus(response).then(() => {
resolve(response);
});
}
});
};
});
};
// Check saved global security word when changing RGAs
async function secword(){
if (GM_getValue('globalSecurityWord')){
const savedSecWord = GM_getValue('globalSecurityWord');
const transfer = await superfetch("pointtransfer");
if (transfer.match(/Security Prompt<\/strong>/i)){
const prompt = (transfer.match(/name="prompt_number" value="([0-9]+)"/i) || [0,0])[1]
await superpost('security_prompt.php',`prompt_number=${prompt}&answer=${savedSecWord}&security_submitted=Continue`);
};
};
};
async function notes(string,id,server){
// Styling
GM_addStyle(`
#notesText{ resize: none; width:600px; height:250px; }
`)
// Get char/crew name and build file name
const name = string.replace(/<.*?>/g,'').replace(/[\n\r]/g,'').replace(/ /g,'');
const fileName = `mmNote(${name},${id},${server})`
// Add button to toolbar
document.querySelector("#notesBtnDiv").innerHTML = ` `
// Open notes
document.querySelector("#notesDiv").addEventListener('click', async function(){
createWindow(`${name} Notes`, "notes", 600, 250, 0);
document.querySelector("#notes_content").innerHTML =
``
// Load existing note if found
if (GM_getValue(fileName)){
document.querySelector("#notesText").value = GM_getValue(fileName);
};
// Focus in the text area
document.querySelector("#notesText").focus();
// Save on close
document.querySelector('img[onclick="closeWindow(\'notes\');"]').outerHTML = ' '
document.querySelector("#saveAndClose").setAttribute('onmouseover',`statspopup(event,'Save and close ')`)
document.querySelector("#saveAndClose").setAttribute('onmouseout',`kill()`)
document.querySelector("#saveAndClose").setAttribute('style','cursor:pointer')
document.querySelector("#saveAndClose").addEventListener('click', function(){
const noteContent = document.querySelector("#notesText").value;
GM_setValue(fileName,noteContent);
document.querySelector("#notes").remove();
});
});
};
// On/Off toggle function
async function onOff(name){
document.querySelector("#mmOnOffDiv").innerHTML = ` `
// Build functions
const toggle = GM_getValue(`onOff(${name})`);
if (toggle == "false"){
document.querySelector("#mmOnOff").setAttribute('onmouseover',`statspopup(event,'Turn on MoxxiMod for this page')`);
document.querySelector("#mmOnOff").setAttribute('onmouseout','kill()');
document.querySelector("#mmOnOff").style.filter = 'saturate(0) opacity(0.5)'
document.querySelector("#mmOnOff").addEventListener('click', async function(){
GM_setValue(`onOff(${name})`,'true');
window.location = window.location;
});
} else {
document.querySelector("#mmOnOff").setAttribute('onmouseover',`statspopup(event,'Turn off MoxxiMod for this page')`);
document.querySelector("#mmOnOff").setAttribute('onmouseout','kill()');
document.querySelector("#mmOnOff").addEventListener('click', async function(){
GM_setValue(`onOff(${name})`,'false');
window.location = window.location;
});
};
};
// Ctrl click select checkboxes
async function ctrlDragCheckboxToggle() {
const checkForBoxes = document.querySelector('input[type="checkbox"]:not([disabled])');
if (!checkForBoxes){
return;
};
// Function
let startX, startY, selectionBox;
let isDragging = false;
let toggleTo = null;
function createSelectionBox() {
const box = document.createElement('div');
box.style.position = 'absolute';
box.style.border = '2px dashed #007bff';
box.style.backgroundColor = 'rgba(0, 123, 255, 0.1)';
box.style.zIndex = '9999';
box.style.pointerEvents = 'none';
document.body.appendChild(box);
return box;
}
function rectIntersects(rect1, rect2) {
return !(rect2.left > rect1.right ||
rect2.right < rect1.left ||
rect2.top > rect1.bottom ||
rect2.bottom < rect1.top);
}
document.addEventListener('mousedown', function(e) {
if (!e.ctrlKey || e.button !== 0) return; // Only activate on Ctrl + Left Click
isDragging = true;
startX = e.pageX;
startY = e.pageY;
// Prevent text selection during drag
document.body.style.userSelect = 'none';
document.body.style.webkitUserSelect = 'none';
document.body.style.msUserSelect = 'none';
const target = document.elementFromPoint(e.clientX, e.clientY);
if (target && target.type === 'checkbox') {
toggleTo = !target.checked;
}
selectionBox = createSelectionBox();
selectionBox.style.left = `${startX}px`;
selectionBox.style.top = `${startY}px`;
});
document.addEventListener('mousemove', function(e) {
if (!isDragging) return;
const x = Math.min(e.pageX, startX);
const y = Math.min(e.pageY, startY);
const width = Math.abs(e.pageX - startX);
const height = Math.abs(e.pageY - startY);
selectionBox.style.left = `${x}px`;
selectionBox.style.top = `${y}px`;
selectionBox.style.width = `${width}px`;
selectionBox.style.height = `${height}px`;
});
document.addEventListener('mouseup', function() {
if (!isDragging) return;
isDragging = false;
// Restore text selection
document.body.style.userSelect = '';
document.body.style.webkitUserSelect = '';
document.body.style.msUserSelect = '';
const boxRect = selectionBox.getBoundingClientRect();
document.querySelectorAll('input[type="checkbox"]:not([disabled])').forEach(checkbox => {
const rect = checkbox.getBoundingClientRect();
if (
rectIntersects(boxRect, rect) &&
!checkbox.disabled &&
checkbox.offsetParent !== null // ensures visible
) {
if (checkbox.checked !== toggleTo) {
checkbox.click(); // triggers real UI behavior
}
}
});
if (selectionBox) {
selectionBox.remove();
selectionBox = null;
}
toggleTo = null;
});
// Tool tip
await toolTip(`Ctrl + Left Click and drag your mouse to easily select checkboxes`);
};
// Toolbar tooltip
async function toolTip(content){
document.querySelector("#tipsBtnDiv").innerHTML = ` `
};
// Slider element and animation at the top of the screen to display authentication status
async function displayAuthStatus(status) {
const session = document.querySelector("#rg_sess_id").value;
if (authSliderCreated == false) {
var newDiv = document.createElement('div');
newDiv.id = 'authSlider';
document.body.appendChild(newDiv);
};
if (status.match("NotAuthed") && session != "null") {
document.querySelector("#authSlider").innerHTML = `THIS RGA IS NOT SUBSCRIBED TO MOXXIMOD+`;
document.querySelector("#authSlider").setAttribute('style', 'background:#870000;');
} else {
document.querySelector("#authSlider").innerHTML = `THANK YOU FOR SUBSCRIBING TO MOXXIMOD+`;
document.querySelector("#authSlider").setAttribute('style', 'background:#008700;');
};
// Remove previous styles
GM_addStyle("#authSlider {animation: none;}");
// Trigger reflow to ensure styles are properly reset before applying new animation
document.querySelector("#authSlider").offsetHeight;
// Slider animation
GM_addStyle("#authSlider {animation: authSliderAnimation 4s ease forwards;}");
GM_addStyle("@keyframes authSliderAnimation {0% {position:fixed;bottom:-35px;} 50% {position:fixed;bottom:0px;} 100% {position:fixed;bottom:-35px;}}");
authSliderCreated = true;
};
// Filter tables function
async function filterTables(acceptPartialMatch) {
// Note: table must contain the class "filterable" and the tds to be filtered should all contain the class "filt"
const filterInput = document.getElementById('filter');
const tableRows = document.querySelectorAll('.filterable tbody tr');
filterInput.addEventListener('input', function() {
const filterValue = this.value.trim().toLowerCase();
tableRows.forEach(row => {
const cells = row.querySelectorAll('.filt');
let matched = false;
cells.forEach(cell => {
const values = cell.textContent.split(',').map(item => item.trim().toLowerCase());
if (acceptPartialMatch){
matched = values.some(value => value.includes(filterValue));
} else {
matched = values.includes(filterValue);
};
});
if (filterValue === '' || matched) {
row.style.display = '';
} else {
row.style.display = 'none';
};
});
});
};
// Sort function
let mvOrderSaveArray = ''
async function sortableTables() {
document.querySelectorAll('table.sortable').forEach(table => {
Array.from(table.querySelectorAll('thead th')).forEach(header => {
header.addEventListener('click', event => {
const columnIndex = header.cellIndex;
const tbody = table.querySelector('tbody');
const rows = Array.from(tbody.querySelectorAll('tr'));
const isAscending = !header.classList.contains('ascending');
if (allNumeric(rows, columnIndex)) {
sortNumeric(rows, columnIndex, isAscending);
} else {
sortAlphabetical(rows, columnIndex, isAscending);
}
rows.forEach(row => tbody.removeChild(row));
rows.forEach(row => tbody.appendChild(row));
table.querySelectorAll('thead th').forEach(th => {
th.classList.remove('ascending', 'descending');
});
header.classList.toggle('ascending', isAscending);
header.classList.toggle('descending', !isAscending);
mvTableOrder();
});
header.style.cursor = 'pointer';
});
});
function allNumeric(rows, columnIndex) {
for (let row of rows) {
const cellValue = row.cells[columnIndex].textContent.trim().replace(/,/g, '');
if (isNaN(parseFloat(cellValue))) {
return false;
}
}
return true;
}
function sortNumeric(rows, columnIndex, isAscending) {
rows.sort((a, b) => {
const aValue = parseFloat(a.cells[columnIndex].textContent.replace(/,/g, '').trim());
const bValue = parseFloat(b.cells[columnIndex].textContent.replace(/,/g, '').trim());
return isAscending ? aValue - bValue : bValue - aValue;
});
}
function sortAlphabetical(rows, columnIndex, isAscending) {
rows.sort((a, b) => {
const aValue = a.cells[columnIndex].textContent.trim();
const bValue = b.cells[columnIndex].textContent.trim();
return isAscending ? aValue.localeCompare(bValue) : bValue.localeCompare(aValue);
});
}
// MV table sort saving
const mvTable = document.querySelector("#mvTable");
// Save row order every time the table is sorted
function mvTableOrder(){
if (mvTable){
const order = document.querySelectorAll("#mvTable > tbody > tr > td:nth-child(1) > a")
const array = [];
order.forEach(td => {
array.push(td.innerHTML)
});
mvOrderSaveArray = array;
};
};
// When new table is created, sort it
if (mvTable && mvOrderSaveArray){
const tbody = mvTable.querySelector('tbody');
const rowMap = {};
mvTable.querySelectorAll('tbody > tr').forEach((row, index) => {
const cellValue = row.querySelector('td:first-child').textContent.trim();
rowMap[cellValue] = row;
});
tbody.innerHTML = '';
mvOrderSaveArray.forEach(value => {
const row = rowMap[value];
if (row) {
tbody.appendChild(row);
}
});
}
// Add arrow to column headers
GM_addStyle(`table.sortable th:not(:empty):after {content: "▾";margin-left: 0.1em;}`);
};
async function superfetchItem(itemid){
const itemData = {};
const html = (await superfetch(`item_rollover.php?id=${itemid}`)).replace(/ \(\+[0-9]+\)<\/span>/g,'').replace(//g,'').replace(/,|%/g,'');
itemData.slot = (html.match(/\[Slot - (.*?)\] /i) || ['','slot unknown'])[1].toLowerCase();
itemData.img = (html.match(/ /i) || ['',''])[1];
itemData.rarity = (html.match(/text-shadow: #47462E 1px 1px 2px;color:#([A-Za-z0-9]+)/i) || ['',''])[1];
itemData.name = (html.match(/align="left">(.*?)<\/td>/i) || ['',''])[1].replace('td colspan="2"','a');
itemData.cloned = html.match(/\[Cloned: .*?\]/i) ? true : false;
itemData.clonedset = itemData.cloned ? html.match(/\[Cloned: (.*?)\]/i)[1] : "None";
itemData.openaugs = html.match(/\/images\/augslot\.jpg/) ? html.match(/\/images\/augslot\.jpg/g).length : 0;
itemData.augids = (html.match(/event'[0-9]+_[0-9]+'/g) || []).map(i => i.match(/event'([0-9]+_[0-9]+)/i)[1]);
itemData.augs = (html.match(/src="[^"]*" ONMOUSEOVER="itempopup\(event'[0-9]+_[0-9]+'\)"/g) || []).map(i => ` `).join('')
itemData.gems = 4-((html.match(/src="\/images\/gemslot2\.jpg"/g) || []).length);
itemData.atk = parseInt((html.match(/\+([0-9]+) ATK /i) || [0,0])[1]);
itemData.hp = parseInt((html.match(/\+([0-9]+) HP /i) || [0,0])[1]);
itemData.holydmg = parseInt((html.match(/\+([0-9]+) Holy<\/span>/i) || [0,0])[1]);
itemData.arcanedmg = parseInt((html.match(/\+([0-9]+) Arcane<\/span>/i) || [0,0])[1]);
itemData.shadowdmg = parseInt((html.match(/\+([0-9]+) Shadow<\/span>/i) || [0,0])[1]);
itemData.firedmg = parseInt((html.match(/\+([0-9]+) Fire<\/span>/i) || [0,0])[1]);
itemData.kineticdmg = parseInt((html.match(/\+([0-9]+) Kinetic<\/span>/i) || [0,0])[1]);
itemData.ele = itemData.holydmg + itemData.arcanedmg + itemData.shadowdmg + itemData.firedmg + itemData.kineticdmg
itemData.chaosdmg = parseInt((html.match(/\+([0-9]+) Chaos<\/span>/i) || [0,0])[1]);
itemData.holyres = parseInt((html.match(/\+([0-9]+) Holy Resist/i) || [0,0])[1]);
itemData.arcaneres = parseInt((html.match(/\+([0-9]+) Arcane Resist/i) || [0,0])[1]);
itemData.shadowres = parseInt((html.match(/\+([0-9]+) Shadow Resist/i) || [0,0])[1]);
itemData.fireres = parseInt((html.match(/\+([0-9]+) Fire Resist/i) || [0,0])[1]);
itemData.kineticres = parseInt((html.match(/\+([0-9]+) Kinetic Resist/i) || [0,0])[1]);
itemData.resist = itemData.holyres + itemData.arcaneres + itemData.shadowres + itemData.fireres + itemData.kineticres;
itemData.chaosres = (html.match(/\+([0-9]+) Chaos Resist/i) || [0,0])[1];
itemData.vile = parseInt((html.match(/\+([0-9]+) vile energy/i) || [0,0])[1]);
itemData.rpt = parseInt((html.match(/\+([0-9]+) rage per hr/i) || [0,0])[1]);
itemData.ept = parseInt((html.match(/\+([0-9]+) exp per hr/i) || [0,0])[1]);
itemData.rampage = (html.match(/\+([0-9]+) rampage/i) || [0,0])[1];
itemData.critical = (html.match(/\+([0-9]+) critical hit/i) || [0,0])[1];
itemData.maxrage = parseInt((html.match(/\+([0-9]+) max rage/i) || [0,0])[1]);
itemData.block = (html.match(/\+([0-9]+) block/i) || [0,0])[1];
itemData.eleblock = (html.match(/\+([0-9]+) elemental block/i) || [0,0])[1];
itemData.ps = (html.match(/\+(\d+(\.\d+)?) perfect strike/i) || [0,0])[1];
itemData.raidbound = html.match("Available to") ? true : false;
if (itemData.raidbound){
itemData.raidboundArray = html.match(/ [A-Za-z0-9]+/g).map(i => (i.match(/ ([A-Za-z0-9]+)/)[1]));
};
return itemData;
};
async function superfetchProfile(path){
// Build empty data object
const profileData = {};
const html = (await superfetch(path)).replace(/[\n\r]/g,'').replace(/,/g,'');
// Parse public profile statistic information
profileData.charid = (html.match(/allies\.php\?uid=([0-9]+)/i) || [0,0])[1];
profileData.name = (html.match(/(.*?)<\/font>/i) || ['er','er'])[1];
profileData.level = parseInt((html.match(/Level ([0-9]+)/i) || [0,0])[1]);
profileData.class = (html.match(/Level [0-9]+ (.*?)<\/font>/i) || ['er','er'])[1];
profileData.exp = parseInt(((html.match(/TOTAL EXPERIENCE.*?<\/tr>/i) || '').toString().match(/>([0-9]+)TOTAL POWER.*?<\/tr>/i) || '').toString().match(/>([0-9]+)ATTACK.*?<\/tr>/i) || '').toString().match(/>([0-9]+)HIT POINTS.*?<\/tr>/i) || '').toString().match(/>([0-9]+)GROWTH YESTERDAY.*?<\/tr>/i) || '').toString().match(/>([0-9]+)ELEMENTAL ATTACK.*?<\/tr>/i) || '').toString().match(/>([0-9]+)ELEMENTAL RESIST.*?<\/tr>/i) || '').toString().match(/>([0-9]+)CHAOS DAMAGE.*?<\/tr>/i) || '').toString().match(/>([0-9]+)WILDERNESS LEVEL.*?<\/tr>/i) || '').toString().match(/>([0-9]+)GOD SLAYER LEVEL.*?<\/tr>/i) || '').toString().match(/>([0-9]+)<[A-Za-z]+ size="1">FACTION.*?<\/tr>/i) || '').toString().replace(/\(\)/i,'(0)');
profileData.faction = (factionLookup.match(/([A-Za-z0-9]+) \([0-9]+\)/i) || ['er','er'])[1];
profileData.loyalty = parseInt((factionLookup.match(/[A-Za-z0-9]+ \(([0-9]+)\)/i) || ['er','er'])[1]);
profileData.thedude = (html.replace(/808080/g,',808080').replace(/event/g,'event,').replace(/EQUIPMENT<\/h5>/i,'').match(/(.*?)/i) || ['er','er'])[1].replace(//g,'');
// Helper function to extract data for each equipped item
function parseSection(html, L, T, W, H) {
const selectorRegex = new RegExp(`left:${L}px;top:${T}px;width:${W}px;height:${H}px;.*?<\/div>`)
const section = html.replace(/\s+/g,'').match(selectorRegex)?.[0] || '';
const id = (section.match(/event'([0-9]+)'/i) || [0,0])[1];
const name = (section.match(/alt="([^"]*)"/i) || ['None','None'])[1];
const img = (section.match(/src="([^"]*)"/i) || ['None','None'])[1];
return { id, name, img };
};
// Apply the helper function for each item element
profileData.core = parseSection(html, 61, 12, 41, 41);
profileData.head = parseSection(html, 118, 7, 62, 46);
profileData.neck = parseSection(html, 197, 12, 41, 41);
profileData.weapon = parseSection(html, 45, 67, 56, 96);
profileData.body = parseSection(html, 121, 67, 56, 96);
profileData.shield = parseSection(html, 198, 67, 56, 96);
profileData.belt = parseSection(html, 61, 192, 41, 41);
profileData.pants = parseSection(html, 118, 175, 62, 75);
profileData.ring = parseSection(html, 197, 192, 41, 41);
profileData.foot = parseSection(html, 118, 262, 62, 66);
profileData.gem = parseSection(html, 10, 346, 32, 32);
profileData.rune = parseSection(html, 54, 346, 32, 32);
profileData.badge = parseSection(html, 214, 346, 32, 32);
profileData.booster = parseSection(html, 258, 346, 32, 32);
profileData.ccrest = parseSection(html, 9, 9, 60, 60);
profileData.fcrest = parseSection(html, 83, 9, 60, 60);
profileData.pcrest = parseSection(html, 157, 9, 60, 60);
profileData.acrest = parseSection(html, 231, 9, 60, 60);
// Parse orb data
const allOrbs = (html.match(/
.*?<\/div>/i) || '').toString();
const orbIds = (allOrbs.match(/event'[0-9]+'/g) || []).map(i => i.match(/event'([0-9]+)'/i)[1]);
const orbNames = (allOrbs.match(/alt="[^"]*"/g) || []).map(i => i.match(/alt="([^"]*)"/i)[1]);
const orbImgs = (allOrbs.match(/src="[^"]*"/g) || []).map(i => i.match(/src="([^"]*)"/i)[1]);
for (let i = 0; i < 3; i++) {
profileData[`orb${i + 1}`] = {
id: orbIds[i] || 0,
name: orbNames[i] || "None",
img: orbImgs[i] || "None"
};
};
const godKills = (html.match(/.*?/i) || '').toString(); // !IMPORTANT! dont forget commas are removed from god names
profileData.completedgodslayer = (godKills.match(/
.*?<\/b>/g) || ['None ']).map(i => i.match(/(.*?)<\/b>/i)[1]).join(',');
const skillsCast = (html.match(/.*?/i) || '').toString();
profileData.skills = {
images: html.match(/.*?/i) ? (html.match(/.*?/i).toString().match(/ /g) || []).map(i => i.replace(/event'/g,`event,'`).replace(/'808080/g,`',808080`)) : [],
list: (skillsCast.match(/alt="[^"]*"/g) || ['"None"']).map(i => i.match(/"([^"]*)"/i)[1]).join(', ')
};
profileData.crewname = (html.match(/(.*?)<\/a>/i) || ['None','None'])[1];
profileData.crewid = parseInt((html.match(/ .*?<\/a>/i) || [0,0])[1]);
profileData.underlings = (((html.match(/ .*?<\/table>/i) || '').toString().match(/profile\.php\?id=[0-9]+/g)) || []).length;
profileData.underlingids = (((html.match(/.*?<\/table>/i) || '').toString().match(/profile\.php\?id=[0-9]+/g)) || ['0']).map(i => i.match(/[0-9]+/)[0]);
profileData.profilepic = (html.match(/class="profilepic" src="([^"]*)"/i) || ['er','er'])[1];
if (!path.match(/\?id=/i) && !path.match(/\?transnick=/i)){
profileData.currentrage = parseInt((html.replace(/<.*?>/g,'').replace(/\s+/g,'').match(/RAGE:([0-9]+)/i) || [0,0])[1]);
profileData.maxrage = parseInt((html.replace(/<.*?>/g,'').replace(/\s+/g,'').match(/Maximum:([0-9]+)/i) || [0,0])[1]);
profileData.rageismaxed = profileData.currentrage == profileData.maxrage; // Returns true if rage is capped
profileData.growthtoday = parseInt((html.replace(/<.*?>/g,'').replace(/\s+/g,'').match(/GrowthToday:([0-9]+)/i) || [0,0])[1]);
profileData.tolevel = parseInt((html.replace(/<.*?>/g,'').replace(/\s+/g,'').match(/NeededtoLevel:([0-9]+)/i) || [0,0])[1]);
profileData.expperturn = parseInt((html.replace(/<.*?>/g,'').replace(/\s+/g,'').match(/PerTurn:([0-9]+)Minimum/i) || [0,0])[1]);
profileData.rageperturn = parseInt((html.replace(/<.*?>/g,'').replace(/\s+/g,'').match(/PerTurn:([0-9]+)Maximum/i) || [0,0])[1]);
profileData.rampage = parseInt((html.replace(/<.*?>/g,'').replace(/\s+/g,'').match(/Rampage:([0-9]+)%/i) || [0,0])[1]);
profileData.critical = parseInt((html.replace(/<.*?>/g,'').replace(/\s+/g,'').match(/Critical:([0-9]+)%/i) || [0,0])[1]);
profileData.gold = parseInt(html.replace(/<.*?>/g,'').replace(/\s+/g,'').match(/Gold:([0-9]+)/i)[1])
}
return profileData;
};
async function superfetchHome(path){
const homeData = {};
const html = (await superfetch(path)).replace(/\s+/g,'').replace(/[\n\r]/g,'');
homeData.name = (html.match(/(.*?)<\/h6>/i) || ['er','er'])[1];
homeData.level = parseInt((html.match(/([0-9]+)<\/span>/i) || [0,0])[1]);
homeData.currentrage = parseInt((html.replace(/<.*?>/g,'').replace(/\s+/g,'').replace(/,/g,'').match(/RAGE:([0-9]+)/i) || [0,0])[1]);
homeData.maxrage = parseInt((html.replace(/<.*?>/g,'').replace(/\s+/g,'').replace(/,/g,'').match(/Maximum:([0-9]+)/i) || [0,0])[1]);
homeData.growthtoday = parseInt((html.replace(/<.*?>/g,'').replace(/\s+/g,'').replace(/,/g,'').match(/GrowthToday:([0-9]+)/i) || [0,0])[1]);
homeData.expperturn = parseInt((html.replace(/<.*?>/g,'').replace(/\s+/g,'').replace(/,/g,'').match(/PerTurn:([0-9]+)Minimum/i) || [0,0])[1]);
homeData.rageperturn = parseInt((html.replace(/<.*?>/g,'').replace(/\s+/g,'').replace(/,/g,'').match(/PerTurn:([0-9]+)Maximum/i) || [0,0])[1]);
homeData.rampage = parseInt((html.replace(/<.*?>/g,'').replace(/\s+/g,'').match(/Rampage:([0-9]+)%/i) || [0,0])[1]);
homeData.critical = parseInt((html.replace(/<.*?>/g,'').replace(/\s+/g,'').match(/Critical:([0-9]+)%/i) || [0,0])[1]);
homeData.skillpoints = parseInt(html.match(/Skill:<\/b>([0-9]+)<\/div>/i)[1]);
homeData.skillclass = html.replace(/PopStar|Monster|Gangster/g,'').match(/Level[0-9]+([A-Za-z]+)<\/span>/i)[1];
homeData.holyresist = parseInt(html.match(/HolyResist:<\/b><\/font><\/td>([0-9]+)<\/font>/i)[1]);
homeData.arcaneresist = parseInt(html.match(/ArcaneResist:<\/b><\/font><\/td>([0-9]+)<\/font>/i)[1]);
homeData.shadowresist = parseInt(html.match(/ShadowResist:<\/b><\/font><\/td>([0-9]+)<\/font>/i)[1]);
homeData.fireresist = parseInt(html.match(/FireResist:<\/b><\/font><\/td>([0-9]+)<\/font>/i)[1]);
homeData.kineticresist = parseInt(html.match(/KineticResist:<\/b><\/font><\/td>([0-9]+)<\/font>/i)[1]);
homeData.chaosresist = parseInt(html.match(/ChaosResist:<\/b><\/font><\/td>([0-9]+)<\/font>/i)[1]);
homeData.alvar = parseInt(html.replace(/<.*?>/g,'').match(/Alvar:([0-9]+)/i)[1]);
homeData.delruk = parseInt(html.replace(/<.*?>/g,'').match(/Delruk:([0-9]+)/i)[1]);
homeData.vordyn = parseInt(html.replace(/<.*?>/g,'').match(/Vordyn:([0-9]+)/i)[1]);
homeData.codex = parseInt(html.replace(/<.*?>/g,'').match(/TriworldInfluence:([0-9]+)/i)[1]);
homeData.underlingloyalty = html.match(/UnderlingLoyalty:<\/b><\/font><\/td>([0-9]+)/i)[1];
homeData.mrupgrades = {
purchased: parseInt((html.replace(/,/g,'').match(/MaximumRage:<\/b><\/font><\/td>([0-9]+)\/[0-9]+/i) || [0,0])[1]),
max: parseInt((html.replace(/,/g,'').match(/MaximumRage:<\/b><\/font><\/td>[0-9]+\/([0-9]+)/i) || [0,0])[1])
};
return homeData;
};