// ==UserScript== // @name MoxxiMod 8 // @namespace https://studiomoxxi.com/ // @description one click at a time // @author Ben // @match *.outwar.com/* // @version 8.26 // @grant GM_xmlhttpRequest // @license MIT // @run-at document-start // @grant GM_getResourceText // @grant GM_addStyle // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_listValues // @grant GM_xmlhttpRequest // @grant GM_addValueChangeListener // @grant unsafeWindow // @require http://code.jquery.com/jquery-3.6.0.min.js // @downloadURL none // ==/UserScript== /* globals jQuery, $, waitForKeyElements */ const url = document.URL; // Disable on Outwar pages without standard Outwar UI if ( url.match("mob.php") || url.match("mob_talk") || url.match("rampidgaming.outwar") || url.match(/attack\/[0-9]+/i) ){ return }; // Disable on Outwar if loaded in an iframe if (window.self !== window.top) { return; } // Load custom theme loadCustomTheme(); // Global styles if (url.match("outwar")) { // Global styles GM_addStyle (` body > center > div.sub-header-container{display:none;z-index:9999;} body > center > div.header-container.fixed-top{display:none;} body > center > div.sub-header-container2{display:none;} body > center > div.sub-header-container > header{height:46px;box-shadow:0 5px 5px 0 rgba(0,0,0,1) !important;} #accordionExample{display:none;} body > center > div.sub-header-container{top:0px} #container{position:relative;top:-70px} #sidebar ul.menu-categories.ps{border-right:0px !important;top:-90px;z-index:100;} body > center > div.sub-header-container > header > ul.navbar-nav.flex-row.mr-auto.toolbar-nav > li:nth-child(1) > div > span{width:150px !important;} span.toolbarSpan{margin-left:7px;vertical-align:middle;} .form-control-new{display:inline-block;border: 1px solid #1b2e4b;color:#e6e6e6;letter-spacing:1px;padding-left:10px;padding-right:10px;background:#1b2e4b;font-size:18px;box-shadow: 0 0 3px rgba(0, 0, 0, 0.5);border-radius:5px;transition: .5s ease-out;} .form-control-new:hover{opacity:0.5;} .navbar{min-height: 0px !important;} @keyframes gradientScroll { 0% {background-position: 0% 50%;} 50% {background-position: 100% 50%;} 100% {background-position: 0% 50%;} } .sub-header-container .navbar { -webkit-box-shadow: 0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12),0 3px 5px -1px rgba(0,0,0,.2); -moz-box-shadow: 0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12),0 3px 5px -1px rgba(0,0,0,.2); box-shadow: 0 6px 10px 0 rgba(0,0,0,.14),0 1px 18px 0 rgba(0,0,0,.12),0 3px 5px -1px rgba(0,0,0,.2); background: linear-gradient(270deg, #1B2E4B, #1a1c2d, #1B2E4B); /* Define the gradient colors */ background-size: 300% 300%; /* Set the size larger than 100% to create the scrolling effect */ background-position: 0% 50%; /* Start position of the gradient */ animation: gradientScroll 5s linear infinite; /* 5 seconds animation duration, moves left to right infinitely */ border-radius: 0; padding: 0; justify-content: flex-start; min-height: 53px; width: 100%; } .pop td {text-align:left;padding:2px;font-size:11px;color:#FFFFFF;} #language-dropdown > div > p.top-exp.mr-1.menu_mid_size{display:none !important;} #language-dropdown > div > p.top-rage{display:none;} body > center > div.sub-header-container > header > ul.navbar-nav.flex-row.mr-auto.toolbar-nav > li:nth-child(1) > div > span{display:none;} li.toolbarButtons{margin-right:13px;} #dropdownCharacters{width:230px !important;margin-left:1rem;font-weight:700;visibility:visible !important;} #dropdownRgas{visibility:visible !important;} .btn-mm{background-color:#060818;margin-left:3px;margin-right:3px;color:#FFFFFF;padding:.4375rem 1.25rem;font-size:14px;font-weight:400;transition: .5s ease-out;cursor:pointer;border-radius:10px;box-shadow: 0 5px 20px 0 rgba(0,0,0,.1);letter-spacing:2px;} .btn-mm:hover{filter: saturate(250%);opacity:0.75;} .pointer{cursor:pointer;} #content > div.footer-wrapper{display:none;} #container > div.footer-wrapper{display:none;} body img[src*="items/skillitem.jpg"] {content: url("https://studiomoxxi.com/moxximod/aneuro.webp") !important;} body img[src*="items/questshard.jpg"] {content: url("https://studiomoxxi.com/moxximod/newqshard.gif") !important;} body img[src*="images/potion22.jpg"] {content: url("https://studiomoxxi.com/moxximod/firewater_new.webp") !important;} table.table{margin-bottom: 0rem;} .little-space{margin-top:0px;} .table td, .table th{padding:8px !important;} .bio .widget-content-area h3:after{display:none;} img[src*="images/toolbar/Attacked.png"] {display: none;} `); // Toolbar tiles GM_addStyle (` #toolbarTiles img{width:30px;height:30px;border-radius:5px;margin-right:3px;margin-left:3px;} img.tile-still:hover{filter: saturate(250%);opacity:0.5;} img.tile-animate{cursor:pointer;transition: .5s ease-out;animation: fadeIn 1s ease-in-out forwards, tileanimate 1s infinite;box-shadow: 0 0 3px rgba(0, 0, 0, 0.5);} img.tile-still{cursor:pointer;transition: .5s ease-out;animation: fadeIn 1s ease-in-out forwards;box-shadow: 0 0 3px rgba(0, 0, 0, 1);} @keyframes fadeIn {from {opacity: 0;} to {opacity: 1;}} @keyframes tileanimate{0% {filter: saturate(100%);} 50% {filter: saturate(250%); } 100% {filter: saturate(100%);}} `); // Toolbar fast travel menu GM_addStyle (` #travelDiv{z-index:5;height:0px;box-shadow: 0 0 10px rgba(0, 0, 0, 1);overflow:auto;text-align:center;display:inline-block;} .travel-destination{width:300px;margin-bottom:7px;height:40px;padding:5px;text-align:left;} img.travel-img{height:25px;width:25px;border-radius:5px;margin-right:15px;margin-left:5px;box-shadow: 0 0 3px rgba(0, 0, 0, 0.5);} `); // Toolbar moxximod apps menu GM_addStyle (` #appsDiv{z-index:5;height:0px;box-shadow: 0 0 10px rgba(0, 0, 0, 1);overflow:hidden;text-align:left;} div.appDiv{display:inline-block;font-size:9px;text-align:center;font-family:monospace,monospace;width:95px;} img.app-img{background: linear-gradient(to bottom, #898989, #262626);height:60px;width:60px;margin:7px;border-radius:10px;padding:10px;box-shadow: 0 0 3px rgba(0, 0, 0, 0.5);box-shadow: inset 0.4em 0.4em 0.4em 0 rgba(255,255,255,0.8), inset -0.4em -0.4em 0.4em 0 rgba(0,0,0,0.8);cursor:pointer} img.app-img:hover{filter: saturate(250%);opacity:0.75;transition: .5s ease-out;} `); // Toolbar quick caster menu GM_addStyle (` #casterDiv{z-index:5;height:0px;box-shadow: 0 0 10px rgba(0, 0, 0, 1);overflow:hidden;} img.castable{height:40px;width:40px;margin:3px;border-radius:8px;box-shadow: 0 0 5px rgba(0, 0, 0, 1);background:#000000;cursor:pointer;transition: 0.25s ease-out;border: 2px #475254 SOLID;} img.castable:hover{filter: saturate(250%);opacity:0.5;} img.caster-selected{border:4px #00CC00 SOLID !important;width:40px !important;height:40px !important;padding:5px !important;} `); // Toolbar search menu GM_addStyle (` #searchDiv{z-index:0;height:0px;box-shadow: 0 0 10px rgba(0, 0, 0, 1);overflow:hidden;text-align:center;} #searchDiv > form > input,#searchDiv > input{font-size:18px;} `); // Item gems and augments GM_addStyle (` #itemtable > tbody > tr:nth-child(2) > td:nth-child(2) > img:nth-child(1){min-width:58px;} body img[src*="gem_green1"] {content: url("https://studiomoxxi.com/ow_themes/custom_jobs/classic_01/gem_green.webp") !important;width: 25px !important;margin:2px;border:0px solid #666666;margin-top:5px;} body img[src*="gem_blue2"] {content: url("https://studiomoxxi.com/ow_themes/custom_jobs/classic_01/gem_blue.webp") !important;width: 25px !important;margin:2px;border:0px solid #666666;margin-top:5px;} body img[src*="gem_red2"] {content: url("https://studiomoxxi.com/ow_themes/custom_jobs/classic_01/gem_red.webp") !important;width: 25px !important;margin:2px;border:0px solid #666666;margin-top:5px;} body img[src*="gem_white2"] {content: url("https://studiomoxxi.com/ow_themes/custom_jobs/classic_01/gem_white.webp") !important;width: 25px !important;margin:2px;border:0px solid #666666;margin-top:5px;} body img[src*="gemslot2"] {width: 30px !important;margin-bottom:5px;border:1px solid #666666;} #itemtable>tbody>tr:nth-child(2)>td:nth-child(2)>img:nth-child(8) {width: 25px !important;margin:4px !important;} #itemtable>tbody>tr:nth-child(2)>td:nth-child(2)>img:nth-child(9) {width: 25px !important;margin:4px !important;} #itemtable>tbody>tr:nth-child(2)>td:nth-child(2)>img:nth-child(10) {width: 25px !important;margin:4px !important;} #itemtable>tbody>tr:nth-child(2)>td:nth-child(2)>img:nth-child(11) {width: 25px !important;margin:4px !important;} #itemtable>tbody>tr:nth-child(2)>td:nth-child(2)>img:nth-child(12) {width: 25px !important;margin:4px !important;} `); // Moxximod+ auth div GM_addStyle (` #authSlider{height:28px;padding:3px;z-index:99999;box-shadow: 0 0 10px rgba(0, 0, 0, 1);overflow:hidden;width:100%;text-align:center;color:#FFFFFF;position:fixed;font-weight:500 !important;font-size:16px;bottom:0px;} `) // Moxximod table cells GM_addStyle (` .mm-cell{background-color:#060818;} `) // Blank overlay GM_addStyle (` #blankOverlay{height:100%;width:100%;z-index:1000;position:fixed;top:0px;border-radius:0px;opacity:0.99;} `) // Loading overlay GM_addStyle (` #loadingOverlay{height:100%;width:100%;z-index:9999;position:fixed;top:0px;border-radius:0px;opacity:0.85;} #loadingOverlay > img{position:absolute;top:50%;left:50%;transform: translate(-50%,-50%);} `) // Button dropdown menus GM_addStyle (` .dropdown-menu{box-shadow: 0px 0px 10px rgba(0, 0, 0, 1);} `) // Images to remove GM_addStyle(` img[src="images/store_head.jpg"]{display:none;} img[src="images/page/gladiator/Gladiator.png"]{display:none;} `) }; // Global variables let authSliderCreated = false; let superfetchCache = {} // Functions to run before page loads if (window.location.href.toString().match(/outwar\.com\/characters\/[0-9]+/i)){window.location = window.location.href.replace("characters/","profile?id=")}; // Hide native #content $('head').append(''); // Run MoxxiMod once #container element is loaded on Outwar let didPageLoadedRun = false; var observer = new MutationObserver(function(mutationsList, observer) { if (document.querySelector("#container")) { didPageLoadedRun = true; pageLoaded(); observer.disconnect(); }; }); observer.observe(document, { childList: true, subtree: true }); // Run pageLoaded() after 2 seconds if it hasn't run already setTimeout(function() { if (!didPageLoadedRun){ didPageLoadedRun = true; pageLoaded(); console.log('MOXXIMOD: Triggered 2 second lag timer'); }; }, 2000); // Root function async function pageLoaded() { // Server number const server = window.location.toString().match('sigil') ? "sigil" : "torax"; const serverNo = server == "sigil" ? "1" : "2"; // Session ID const rgSessId = document.body.innerHTML.match(/rg_sess_id=([A-Za-z0-9]+)"/i); const rgaName = rgSessId ? rgSessId[1] : "null"; // Toolbar data const toolbarData = document.querySelector("body > center > div.sub-header-container > header > ul.navbar-nav.flex-row.mr-auto.toolbar-nav").innerHTML.replace(/[\n\r]/g,''); // Moxximod core function async function moxximod(){ // Char ID and fetch quest backpack if charid has changed const charId = (document.body.innerHTML.match(/outwar\.com\/page\?x=([0-9]+)/i) || [0,0])[1]; // Global toolbar data const dataTime = toolbarData.match(/

(.*?)<\/p>/i)[1]; const dataLevel = toolbarData.match(/(.*?)<\/span>/i)[1]; const dataRage = parseInt(toolbarData.replace(/,/g,'').match(/(.*?)<\/span>/i)[1]); const dataExp = parseInt(toolbarData.replace(/,/g,'').match(/(.*?)<\/span>/i)[1]); const dataEnhancementExp = parseInt(toolbarData.replace(/,/g,'').match(/Enhancement Exp:<\/b><\/td>([0-9]+)<\/td><\/tr>/i)[1]); const dataToLevel = toolbarData.match(/Needed to Level:<\/b><\/td>(.*?)<\/td><\/tr>/i)[1]; const dataGrowth = parseInt(toolbarData.replace(/,/g,'').match(/Growth Today:<\/b><\/td>(.*?)<\/td><\/tr>/i)[1]); const dataMinimum = parseInt(toolbarData.replace(/,/g,'').match(/Minimum:<\/b><\/td>([0-9]+)<\/td><\/tr>/i)[1]); const dataExpPerTurn = parseInt(toolbarData.replace(/,/g,'').match(/Growth Today:<\/b><\/td>.*?<\/td><\/tr>Per Turn:<\/b><\/td>([0-9]+)<\/td><\/tr>/i)[1]); const dataRagePerTurn = parseInt(toolbarData.replace(/,/g,'').match(/Per Turn:<\/b><\/td>([0-9]+)<\/td><\/tr>Maximum:/i)[1]); const dataMaximum = parseInt(toolbarData.replace(/,/g,'').match(/Maximum:<\/b><\/td>([0-9]+)<\/td><\/tr>/i)[1]); const dataPremium = parseFloat(toolbarData.replace(/,/g, '').match(/Premium:<\/b><\/td>.*?\.<\/font>[0-9]+<\/font>/i).toString().replace("","").replace("Premium:","").replace("","")); const dataAttack = parseInt(toolbarData.replace(/,/g,'').match(/Attack:<\/b><\/td>([0-9]+)<\/td><\/tr>/i)[1]); const dataHp = parseInt(toolbarData.replace(/,/g,'').match(/Hit Points:<\/b><\/td>([0-9]+)<\/td><\/tr>/i)[1]); const dataCritical = parseInt(toolbarData.replace(/,/g,'').match(/Critical:<\/b><\/td>([0-9]+)%<\/td><\/tr>/i)[1]); const dataRampage = parseInt(toolbarData.replace(/,/g,'').match(/Rampage:<\/b><\/td>([0-9]+)%<\/td><\/tr>/i)[1]); const dataBlock = parseInt(toolbarData.replace(/,/g,'').match(/Block:<\/b><\/td>([0-9]+)%<\/td><\/tr>/i)[1]); // Fetch profile const profileData = await superfetchProfile('profile'); // Global functions await menu(rgaName,charId,serverNo,profileData); await toolbar(rgaName,charId,serverNo,profileData,toolbarData,dataTime,dataLevel,dataRage,dataExp,dataEnhancementExp,dataToLevel,dataGrowth,dataMinimum,dataExpPerTurn,dataRagePerTurn,dataMaximum,dataAttack,dataHp,dataCritical,dataRampage,dataBlock); // Check for outwar error before running page-based functions if (document.body.innerHTML.match('images/ErrorImg.jpg') || document.body.innerHTML.match('img/error.png')){ return; }; // Update stored RGA and confirm auth if new RGA detected await updateRga(rgaName); // Page-based functions if (url.match("profile") && !url.match("crew_profile")) { await onOff("profilePage"); if (GM_getValue("onOff(profilePage)") != "false"){ await profilePage(server) }; } else if (url.match("home") && !url.match("crew_home") && !url.match("homepost")) { if (GM_getValue("onOff(home)") != "false"){ await home(profileData) }; await onOff("home"); } else if (url.match("world")){ if (GM_getValue("onOff(world)") != "false"){ await world(profileData,server) }; await onOff("world"); } else if (url.match("cast_skills")){ if (GM_getValue("onOff(cast_skills)") != "false"){ await castSkills() }; await onOff("cast_skills"); } else if (url.match("crew_profile")){ if (GM_getValue("onOff(crew_profile)") != "false"){ await crewProfile(server) }; await onOff("crew_profile"); } else if (url.match("primegods")){ if (GM_getValue("onOff(primegods)") != "false"){ await primegods(profileData) }; await onOff("primegods"); } else if (url.match("crew_vault")){ if (GM_getValue("onOff(crew_vault)") != "false"){ await crewVault() }; await onOff("crew_vault"); } else if (url.match("trade")){ if (GM_getValue("onOff(trade)") != "false"){ await trade() }; await onOff("trade"); } else if (url.match("godstatus")){ if (GM_getValue("onOff(godstatus)") != "false"){ await godstatus() }; await onOff("godstatus"); } else if (url.match("crew_raidresults")){ if (GM_getValue("onOff(crew_raidresults)") != "false"){ await raidResults() }; await onOff("crew_raidresults"); } else if (url.match("itemtransfer")){ if (GM_getValue("onOff(itemtransfer)") != "false"){ await itemTransfer() }; await onOff("itemtransfer"); } else if (url.match("crew_bossspawns")){ if (GM_getValue("onOff(crew_bossspawns)") != "false"){ await bossSpawns() } await onOff("crew_bossspawns"); } else if (url.match("boss_stats")){ if (GM_getValue("onOff(boss_stats)") != "false"){ await bossStats() }; await onOff("boss_stats"); } else if (url.match("changefaction")){ if (GM_getValue("onOff(changeFaction)") != "false"){ await changeFaction(profileData) }; await onOff("changefaction"); } else if (url.match("augmentequip")){ if (GM_getValue("onOff(augmentequip)") != "false"){ await augmentEquip(profileData,server) }; await onOff("augmentequip"); } else if (url.match("closedpvp")){ if (GM_getValue("onOff(closedpvp)") != "false"){ await closedpvp(profileData,server,charId) }; await onOff("closedpvp"); } else if (url.match("pointtransfer")){ if (GM_getValue("onOff(pointtransfer)") != "false"){ await pointTransfer(dataPremium) }; await onOff("pointtransfer"); } else if (url.match(/gladiator\?mobid=[0-9]+/i)){ if (GM_getValue("onOff(gladiator)") != "false"){ await gladiator(charId,profileData) }; await onOff("gladiator"); } else if (url.match("type=vision")){ await moxxivision(server,serverNo,rgaName,charId); } else if (url.match("type=questreport")){ await blankOverlay(server,serverNo,rgaName,charId); await appQuestReport(server,serverNo,rgaName,charId); } else if (url.match("blacksmith")){ if (GM_getValue("onOff(blacksmith)") != "false"){ await blacksmith(server,serverNo,rgaName,charId) }; await onOff("blacksmith"); } else if (url.match(/raidattack\.php\?raidid=[0-9]+/i)){ await raidattack(); } else if (url.match("spawntimeview")){ await oracle(); } else if (url.match("crew_permissions")){ await crewpermissions(); } else if (url.match("crew_bootmem")){ await bootmem(); }; }; // Main moxximod load function await moxximod(); // Unhide content $('#content').show(); // Ancillary functions await itemOnHover(); await itemDropMenu(server,serverNo,rgaName); await openPotionBp(); await ctrlDragCheckboxToggle(); // Functions to run after page load is displayed in full await charactersNavigation(); await toolbarPointsIcon(toolbarData); const noTiles = ['blacksmith','augmentequip','trade','security_prompt','augmentremove','crew_bootmem'] if (!noTiles.some(tile => url.includes(tile))) { await tiles(); } else { document.querySelector("#toolbarTiles").remove(); }; }; // Profile page rebuild async function profilePage(server){ // Styling GM_addStyle (` #allAugs > img {height:26px;width:26px;margin:1px;border-radius:5px;border:2px #475254 SOLID;} #skillsDiv > img {height:35px;width:35px;margin:1px;border-radius:5px;border:2px #475254 SOLID;} #missingRobot, #skills > img {height:40px;width:40px;margin:3px;border-radius:5px;border:2px #475254 SOLID;} div.card-body{padding:0px;} #divHeaderName > font{text-transform: uppercase;font-size: 1.5em;font-weight: bold;margin-bottom: 1rem;word-wrap: break-word;} #pbuttons{margin-bottom:1rem !important;} #content > div > div:nth-child(1){max-width:350px !important;} #content > div > div:nth-child(2){max-width:800px !important;} .widget{padding:10px !important;} .progress{margin-bottom:0rem;} #UnderlingTable > thead > tr > th{padding:10px;margin:5px;margin-top:10px} #UnderlingTable > tbody > tr > td{padding:10px;margin:5px} #UnderlingTable > thead > tr > th,#UnderlingTable > tbody > tr > td{padding:3px;margin:0px;} #UnderlingTable > thead > tr > th:nth-child(4){display:none;} #UnderlingTable > tbody > tr > td:nth-child(4){display:none;} div.profile-comments{text-align: left !important;} div.crestDiv > img{height:32px;width:32px;} #profileSlayerDiv > div > div > img {height:50px !important;} div.smallSlayerDiv {height:50px;width:50px;overflow:hidden;display:inline-block;} #god_slayer{width:600px;} `); // Build variables let profileHeader = document.querySelector("#divHeader").innerHTML.replace(/[0-9]+ Profile Hits/i,'') const charName = profileHeader.match(/(.*?)<\/font>/i)[1]; const profileInfo = document.querySelectorAll('div.card')[0].innerHTML.replace(`

PLAYER INFO
`,"").replace(``,`
`) const profileEq = document.querySelectorAll('div.card')[1].innerHTML.replace(`
EQUIPMENT
`,"").replace(`/images/thedude.png`,'https://studiomoxxi.com/moxximod/thedudeplus.png').replace('left:214px; top:346px;','left:10px; top:300px;').replace('left:258px; top:346px;','left:54px; top:300px;').replace('','') const profileCrests = document.querySelectorAll('div.card')[2].innerHTML.replace(`
SKILL CRESTS
`,"") const profileMastery = document.querySelectorAll('div.card')[3].innerHTML.replace(`
MASTERIES
`,"") const profilePic = document.querySelectorAll('div.card')[5].innerHTML.match(/src="([^"]*)"/i)[1] const profileSlayer = document.querySelectorAll('div.card')[9].innerHTML.replace(``,`

`).replace(/

<\/div>/g,` onmouseout="kill();">
`) const profileLings = document.querySelector("#UnderlingTable").outerHTML.replace('table table-striped-dark mt-1','') const profileLingsLength = (profileLings.match(/profile\.php\?id=[0-9]+/g) || []).length; const profileSkills = document.querySelector('#divSkillsCast').innerHTML.replace(//i,""); const successfulAttacks = parseInt(profileMastery.replace(/,/g,'').match(/([0-9]+) Successful Attacks/i)[1]); const successfulDefends = parseInt(profileMastery.replace(/,/g,'').match(/([0-9]+) Successfully Defended/i)[1]); const crewHits = parseInt(profileMastery.replace(/,/g,'').match(/([0-9]+) Crew Hits/i)[1]); const expStrip = parseInt(profileMastery.replace(/,/g,'').match(/([0-9]+) Exp Stripped/i)[1]); // Parse individual rows of profile statistics const profileInfoArray = profileInfo.replace(/[\n\r]/g,'').match(/.*?<\/tr>/g); const infoClass = profileInfoArray[0]; const infoExp = profileInfoArray[1]; const infoGrowth = profileInfoArray[2]; const infoPower = profileInfoArray[3]; const infoAttack = profileInfoArray[4]; const infoHp = profileInfoArray[5]; const infoChaos = profileInfoArray[6]; const infoEle = profileInfoArray[7]; const infoResist = profileInfoArray[8]; const infoWilderness = profileInfoArray[9]; const infoParent = profileInfoArray[11]; const infoFaction = profileInfoArray[12]; const infoCrew = profileInfoArray[13]; const strength = profileInfoArray[14] ? profileInfoArray[14].match(/role="progressbar" style="width: ([0-9]+)%;/i) [1] : 0; // Parse and build god slayer row const godslayerLevel = profileInfoArray[10].match(/([0-9]+)<\/font>/i)[1]; const totalGods = (await info('Array of all gods')).length; // Pull char id to use for notes const charId = document.body.innerHTML.match(/trade\?tradeWith=([0-9]+)/i)[1]; let uploadPicLink = ''; if (document.querySelector("#divProfile > div:nth-child(2) > div > div > div.col-xl-8.col-md-7 > div > div:nth-child(1) > div > a") != null){ GM_addStyle (`#uploadPicLinkDiv{margin-bottom:-25px;}`) uploadPicLink = document.querySelector("#divProfile > div:nth-child(2) > div > div > div.col-xl-8.col-md-7 > div > div:nth-child(1) > div > a").outerHTML; }; if (document.querySelector("#divActions > a:nth-child(1)")){ var playerAttack = document.querySelector("#divActions > a:nth-child(1)").outerHTML.replace(`ATTACK`,"").replace(`ATTACK`,"").replace(``,"") var playerName = playerAttack.match(//i)[1] var playerId = playerAttack.match(//i)[2] } if (window.location.href.match("id") == null){ GM_addStyle (`#pbuttons{display:none !important;}`) } if (playerAttack == undefined){GM_addStyle (`#atk_button{display:none !important;}`)} var isplayerpp = "no" if (profileHeader.match(//i) != null){ profileHeader = profileHeader.replace(//i,"") isplayerpp = "yes" } if (isplayerpp == "yes"){ profileHeader = profileHeader.replace(``,``) } else { profileHeader = profileHeader.replace(``,``) }; const notMyProfileButtons = `` // Build HTML content document.querySelector("#content").innerHTML = ` ${notMyProfileButtons}
${profileHeader}
${infoCrew}

${infoClass} ${infoExp} ${infoGrowth}

${infoPower} ${infoChaos} ${infoEle} ${infoResist} ${infoWilderness} ${infoParent}
MAXIMUM RAGE
STRENGTH${strength}%
GOD SLAYER LEVEL${godslayerLevel} / ${totalGods}
UNDERLINGS${profileLingsLength}
FROM EQUIPMENT
ELEMENTAL ATTACK
CHAOS ATTACK
MAXIMUM RAGE
FROM AUGMENTS
ELEMENTAL ATTACK
CHAOS ATTACK
MAXIMUM RAGE
SERVER RANKS
TOTAL POWER RANK
ELEMENTAL RANK
CHAOS DAMAGE RANK
MASTERIES
SUCCESSFUL ATTACKS${successfulAttacks.toLocaleString()}
SUCCESSFUL DEFENDS${successfulDefends.toLocaleString()}
EXPERIENCE STRIPPED${expStrip.toLocaleString()}
`+uploadPicLink+`
EQUIPMENT

${profileEq}

SKILLS AND EFFECTS

${profileSkills}
SLOTTED AUGMENTS

` if (isplayerpp == "yes"){ document.querySelector("#isppspan").innerHTML = `PREFERRED PLAYER` } ; // Crests injection const classCrest = profileCrests.match(/left:9px.*\n(.*)/i) const ferocityCrest = profileCrests.match(/left:83px.*\n(.*)/i) const preservationCrest = profileCrests.match(/left:157px.*\n(.*)/i) const afflictionCrest = profileCrests.match(/left:231px.*\n(.*)/i) if (classCrest){ document.querySelector("#classCrestDiv").innerHTML = classCrest[1] } if (ferocityCrest){ document.querySelector("#ferocityCrestDiv").innerHTML = ferocityCrest[1] } if (preservationCrest){ document.querySelector("#preservationCrestDiv").innerHTML = preservationCrest[1] } if (afflictionCrest){ document.querySelector("#afflictionCrestDiv").innerHTML = afflictionCrest[1] } // Missing potions icon and mouseover const allPotionsInfo = await info("All Potions"); const allPotions = allPotionsInfo.map(([item]) => item); const activePotions = (profileSkills.match(/alt="([^"]*)"/g) || []).map(match => match.slice(5, -1).replace(/'/g,'')); const missingPotions = allPotions.filter(item => !activePotions.includes(item)); const missingString = missingPotions.map((item, index) => (index > 0 && index % 2 === 0) ? `
${item}` : item).join(', ').replace(/'/g,''); document.querySelector("#missing").innerHTML = `` if (GM_getValue("auth").match("Full")){ document.querySelector("#missingPotions").setAttribute("onmouseover", `statspopup(event,'Missing ${missingPotions.length} Potions

${missingString}')`); } else { document.querySelector("#missingPotions").setAttribute("onmouseover", `statspopup(event,'Missing ${missingPotions.length} Potions

MoxxiMod+ subscribers can see a complete list of uncast potions here')`); }; if (isplayerpp == "yes"){ GM_addStyle (` #divHeaderName{color:#E79A31 !important;} .bg-danger,.bg-primary,.bg-secondary,.bg-success,.bg-warning{background-color:#E79A31 !important;} -moz-box-shadow: 0px 0px 3px 3px rgba(209,156,32,0.5); box-shadow: 0px 0px 3px 3px rgba(209,156,32,0.5);} #content > div > div:nth-child(1) > div:nth-child(1) > div:nth-child(2) > table > tbody > tr:nth-child(4) > td:nth-child(2) > b > font{color:#E79A31 !important;} #content > div > div:nth-child(1) > div:nth-child(1) > div:nth-child(2) > table > tbody > tr:nth-child(5) > td:nth-child(2) > b > font{color:#E79A31 !important;} #content > div > div:nth-child(1) > div:nth-child(1) > div:nth-child(2) > table > tbody > tr:nth-child(6) > td:nth-child(2) > b > font{color:#E79A31 !important;} #content > div > div:nth-child(1) > div:nth-child(1) > div:nth-child(2) > table > tbody > tr:nth-child(8) > td:nth-child(2) > b > font{color:#E79A31 !important;} `) }; // Add faction badges to profiles const faction = infoFaction.toString().match(/([A-Za-z]+) \((.*?)\)/i) const factionLvl = parseInt(faction[2]) let factionImg; let profileFactionImg = []; if (faction[1] == "Vordyn"){ GM_addStyle (`.profile-widget{-webkit-box-shadow:0px 0px 3px 3px rgba(252,41,205,0.3);`) factionImg = 'https://studiomoxxi.com/moxxibots/factions/v.png' } else if (faction[1] == "Delruk"){ GM_addStyle (`.profile-widget{-webkit-box-shadow:0px 0px 3px 3px rgba(255,120,39,0.3);`) factionImg = 'https://studiomoxxi.com/moxxibots/factions/d.png' } else if (faction[1] == "Alvar"){ GM_addStyle (`.profile-widget{-webkit-box-shadow:0px 0px 3px 3px rgba(0,159,255,0.3);`) factionImg = 'https://studiomoxxi.com/moxxibots/factions/a.png' } else { GM_addStyle (`.profile-widget{-webkit-box-shadow:0px 0px 3px 3px rgba(26,26,26,1);`) }; for (let i = 0; i < factionLvl; i++) { profileFactionImg.push(``) }; if (factionLvl >= 1){ document.querySelector("#classLogo").setAttribute("onmouseover", `statspopup(event,'${faction[1]}
Loyalty Level ${factionLvl}')`); document.querySelector("#classLogo").setAttribute("onmouseout", `kill()`); }; document.querySelector("#classLogo").innerHTML = profileFactionImg.join('') // Display all augs on profiles const arrayOfItems = profileEq.match(/itempopup\(event,'[0-9]+'\)/g); getAugs(arrayOfItems); async function getAugs(arrayOfItems){ let augs = [] const itemLoop = async (item) => { const items = await superfetch(`item_rollover.php?id=${item.match(/[0-9]+/i)}`) const allAugs = items.match(/src="[^"]*" ONMOUSEOVER="itempopup\(event,'[0-9]+_[0-9]+'\)"/g) if (allAugs){ for (let i = 0; i < allAugs.length; i++) { augs.push(allAugs[i]) }; }; }; if (arrayOfItems){ await Promise.all(arrayOfItems.map(itemLoop)); document.querySelector("#allAugs").innerHTML = augs.sort().join('').replace(/src=/g,'`) } }; // Underlings pop-up document.querySelector("#lingPop").addEventListener('click', async function(){ createWindow("Underlings", "underling_table", 400, 1000, 1); document.querySelector("#underling_table_content").innerHTML = profileLings; }); // God slayer pop-up document.querySelector("#godslayerPop").addEventListener('click', async function(){ createWindow("God Slayer", "god_slayer", 600, 600, -200); document.querySelector("#god_slayer_content").innerHTML = profileSlayer.replace('

GOD SLAYER
',''); }); // Maximum rage DIV const ajaxMaxRage = await superfetch('ajax/rankings.php?type=char_maxRage'); const regexMaxRage = new RegExp(`"name":"${charName}","stat":([0-9]+)`); const maxrage = ajaxMaxRage.match(regexMaxRage) ? parseInt(ajaxMaxRage.match(regexMaxRage)[1]).toLocaleString() : "Not available"; document.querySelector("#maximumRageDiv").innerHTML = maxrage; // Total power rank DIV const ajaxPower = await superfetch('ajax/rankings.php?type=char_power'); const regexPower = new RegExp(`"name":"${charName}","stat":"[0-9]+","pic":"[^"]*","rank":([0-9]+)`) const power = ajaxPower.match(regexPower) ? ajaxPower.match(regexPower)[1] + " / 100" : "Not ranked"; document.querySelector("#totalPowerRankDiv").innerHTML = power; // Chaos damage rank DIV const ajaxChaos = await superfetch('ajax/rankings.php?type=char_chaos'); const regexChaos = new RegExp(`"name":"${charName}","pic":"[^"]*","stat":[0-9]+,"rank":([0-9]+)`) const chaos = ajaxChaos.match(regexChaos) ? ajaxChaos.match(regexChaos)[1] + " / 100" : "Not ranked"; document.querySelector("#chaosDamageRankDiv").innerHTML = chaos; // Elemental rank DIV const ajaxEle = await superfetch('ajax/rankings.php?type=char_elepower'); const regexEle = new RegExp(`"name":"${charName}","pic":"[^"]*","stat":[0-9]+,"rank":([0-9]+)`) const elemental = ajaxEle.match(regexEle) ? ajaxEle.match(regexEle)[1] + " / 100" : "Not ranked"; document.querySelector("#elementalRankDiv").innerHTML = elemental; // Screenshot document.querySelector("#screenshotButton").addEventListener('click', () => { loadingOverlay(); // Remove screenshot button document.querySelector("#screenshotButton").remove(); // Justify content left GM_addStyle(` .justify-content-center{justify-content:left !important;} #loadingOverlay{opacity:1;} `) // Move the skills and augs under equipment const content = document.querySelector("#content"); content.innerHTML = content.innerHTML.replace(/<\/div>.*?[\n\r].*?
/i,''); // Remove profile picture document.querySelector("#profilePictureDiv").remove(); document.querySelector("#uploadPicLinkDiv").remove(); // Set screenshot target const targetDiv = document.querySelector("#outwarProfileContent"); // Load html2canvas script const script = document.createElement('script'); script.src = 'https://cdnjs.cloudflare.com/ajax/libs/html2canvas/0.4.1/html2canvas.min.js'; document.head.appendChild(script); script.onload = async () => { await captureScreenshot(); }; // Take the screenshot async function captureScreenshot(){ html2canvas(targetDiv, { useCORS: true, logging: true, onrendered: function(canvas) { canvas.toBlob(blob => { if (navigator.clipboard && navigator.clipboard.write) { const item = new ClipboardItem({'image/png': blob}); navigator.clipboard.write([item]).then(() => { window.location = window.location.href; }).catch(err => { alert('Error copying screenshot to clipboard') window.location = window.location.href; }); } else { alert('Clipboard API not supported. Copying screenshot to clipboard is not possible.'); window.location = window.location.href; } }); }}); }; }); statsFromItemsAndAugs(); notes(profileHeader,charId,server); async function statsFromItemsAndAugs(){ // Stats from items and augments let eleFromItems = 0; let eleFromAugs = 0; let chaosFromItems = 0; let chaosFromAugs = 0; let mrFromItems = 0; let mrFromAugs = 0; const augIds = []; const parseEq = profileEq.replace(/[\n\r]/g,'').match(/.*?<\/div>/g).slice(0,10); const itemLoop = async (item) => { const itemid = (item.match(/id=([0-9]+)/i) || [0,0])[1]; if (itemid > 0){ const itemData = await superfetchItem(itemid); eleFromItems += itemData.ele; mrFromItems += itemData.maxrage; chaosFromItems += itemData.chaosdmg; for (let i = 0; i < itemData.augids.length; i++) { const augid = itemData.augids[i]; augIds.push(augid); }; }; }; await Promise.all(parseEq.map(itemLoop)); const augLoop = async (aug) => { const augData = await superfetchItem(aug); eleFromItems -= augData.ele; chaosFromItems -= augData.chaosdmg; eleFromAugs += augData.ele; chaosFromAugs += augData.chaosdmg; mrFromAugs += augData.maxrage; } await Promise.all(augIds.map(augLoop)); document.querySelector("#elementalFromItemsDiv").innerHTML = eleFromItems.toLocaleString(); document.querySelector("#chaosFromItemsDiv").innerHTML = chaosFromItems.toLocaleString(); document.querySelector("#maxRageFromItemsDiv").innerHTML = mrFromItems.toLocaleString(); document.querySelector("#elementalFromAugsDiv").innerHTML = eleFromAugs.toLocaleString(); document.querySelector("#chaosFromAugsDiv").innerHTML = chaosFromAugs.toLocaleString(); document.querySelector("#maxRageFromAugsDiv").innerHTML = mrFromAugs.toLocaleString(); }; }; // Build new left-hand menu async function menu(rgaName,charId,serverNo,profileData){ GM_addStyle (`#accordionExample{display:revert !important;background:#060818;}`) if (document.querySelector("#accordionExample > a")){ const buyPointsLink = document.querySelector("#accordionExample > a").outerHTML.match(/href="([^"]*)">/i)[1]; const crewId = profileData.crewid; const level = profileData.level; document.querySelector("#accordionExample").innerHTML = ` ` // Build crew menu based on whether or not a crew ID was found var ulElement = document.getElementById("menuCrew"); if (crewId > 0){ const liHome = document.createElement("li"); const liProfile = document.createElement("li"); const liPrime = document.createElement("li"); const liRaids = document.createElement("li"); const liBosses = document.createElement("li"); const liVault = document.createElement("li"); const liCap = document.createElement("li"); const liGods = document.createElement("li"); const liLeave = document.createElement("li"); liHome.innerHTML = `CREW HOME` liProfile.innerHTML = `CREW PROFILE` liPrime.innerHTML = `PRIME GODS` liRaids.innerHTML = `RAID RESULTS` liBosses.innerHTML = `RAID BOSSES` liVault.innerHTML = `CREW VAULT` liCap.innerHTML = `CAP STATUS` liGods.innerHTML = `GOD STATUS` liLeave.innerHTML = `LEAVE CREW` ulElement.appendChild(liHome); ulElement.appendChild(liProfile); ulElement.appendChild(liPrime); ulElement.appendChild(liRaids); ulElement.appendChild(liBosses); ulElement.appendChild(liVault); ulElement.appendChild(liCap); ulElement.appendChild(liGods); ulElement.appendChild(liLeave); } else { var liInvites = document.createElement("li"); liInvites.innerHTML = `CREW INVITES` ulElement.appendChild(liInvites); }; if (level >= 91){ const ulElement = document.querySelector("#components"); const newLiElement = document.createElement("li"); newLiElement.innerHTML = `FACTIONS`; ulElement.appendChild(newLiElement); }; // Global security word input if (GM_getValue('globalSecurityWord')){ const savedSecWord = GM_getValue('globalSecurityWord'); document.querySelector("#global_sec_word").value = savedSecWord; }; // Global security word button document.querySelector("#secWordButton").addEventListener('click', async function(){ const inputText = document.querySelector("#global_sec_word").value GM_setValue('globalSecurityWord',inputText); await secword(); alert(`Successfully saved ${inputText} to your browser`) }); }; }; // Fetch challenge token data async function challengeStatus(){ const data = await superfetch(`ajax/challenge_status`); if (data.match('Trustees do not have access to this page')){ data = `{"result":true,"tokens":"NA","p_tokens":"NA","timeleft":59210}` } return data; } // Build new toolbar async function toolbar(rgaName,charId,serverNo,profileData,toolbarData,dataTime,dataLevel,dataRage,dataExp,dataEnhancementExp,dataToLevel,dataGrowth,dataMinimum,dataExpPerTurn,dataRagePerTurn,dataMaximum,dataAttack,dataHp,dataCritical,dataRampage,dataBlock){ // Remove the 3 line thing at the top-left const threeLineThing = document.querySelector("body > center > div.sub-header-container > header > a"); if (threeLineThing){ threeLineThing.remove(); }; // Hide toolbar data DIVs const toolbarDivcount = document.querySelectorAll("li.nav-item.more-dropdown.little-space.hide-on-mob").length for (let i = 3; i < toolbarDivcount+3; i++) { document.querySelector("li.nav-item.more-dropdown.little-space.hide-on-mob:nth-child("+i+")").setAttribute("style","display:none") }; // Create new LI element to add to toolbar const toolbarNewLi = document.createElement('li'); toolbarNewLi.innerHTML = ` `; toolbarNewLi.setAttribute('class','nav-item more-dropdown little-space hide-on-mob') const toolbarUl = document.querySelector("body > center > div.sub-header-container > header > ul.navbar-nav.flex-row.mr-auto.toolbar-nav"); toolbarUl.appendChild(toolbarNewLi); // Day of the week const utcDate = new Date(); const estOffset = 5 * 60; // Offset in minutes const estDate = new Date(utcDate.getTime() - estOffset * 60000); const estFormatter = new Intl.DateTimeFormat('en-US', { timeZone: 'UTC', weekday: 'long' }); const dayOfTheWeek = estFormatter.format(estDate); // Calculate time of max rage const untilMax = Math.ceil((dataMaximum - dataRage) / dataRagePerTurn); const maxTime = (parseInt(dataTime.match(/([0-9]+):/i)[1]) + untilMax) % 12; const maxHour = (untilMax > 0) ? `${maxTime}:00` : "NOW"; // Toolbar mouseover tables const expTable = `
EXPERIENCE:${dataExp.toLocaleString()}
ENHANCEMENT EXP:${dataEnhancementExp.toLocaleString()}
NEEDED TO LEVEL:${dataToLevel}
GROWTH TODAY:${dataGrowth.toLocaleString()}
MINIMUM EXP:${dataMinimum.toLocaleString()}
PER TURN:${dataExpPerTurn.toLocaleString()}
` const rageTable = `
MAXIMUM:${dataMaximum.toLocaleString()}
WILL MAX:${maxHour}
PER TURN:${dataRagePerTurn.toLocaleString()}
ATTACK:${dataAttack.toLocaleString()}
HIT POINTS:${dataHp.toLocaleString()}
CRITICAL:${dataCritical.toLocaleString()}%
RAMPAGE:${dataRampage.toLocaleString()}%
BLOCK:${dataBlock.toLocaleString()}%
` // Moxximod+ RGA dropdown menu if (GM_getValue("savedRgas") == undefined) {GM_setValue("savedRgas","no sessions saved")} if (GM_getValue("savedRgas") == "no sessions saved"){ document.querySelector("#toolbarRgas").remove() } else { // Build RGA dropdown menu const options = GM_getValue("savedRgas") document.querySelector("#toolbarRgas").innerHTML = ` ` // RGA dropdown menu function document.getElementById("dropdownRgas").addEventListener("change", function() { var selectedValue = this.value; if (selectedValue !== "") { window.location.href = selectedValue; }; }); }; // Points icon document.querySelector("#toolbarPoints").innerHTML = `💰` // Toolbar clock document.querySelector("#toolbarClock").innerHTML = `${dataTime}` // Toolbar level document.querySelector("#toolbarLvl").innerHTML = `LEVEL: ${dataLevel}` // Toolbar rage document.querySelector("#toolbarRage").innerHTML = `RAGE: ${dataRage.toLocaleString()}` // Check if rage is capped if (toolbarData.match(``)){ document.querySelector("#toolbarRageText").setAttribute("style","border: 2px SOLID #FF0000 !important") }; // Check if ready to level if (dataToLevel.match(/LEVEL!/)){ document.querySelector("#toolbarLvl a").setAttribute("style","border: 2px SOLID #00FF00 !important") }; // Modify the menu button when page isn't maximized document.querySelector("#language-dropdown > svg").outerHTML = `` // Active character top-left button GM_addStyle(` #btnCharacters{width:230px;overflow:hidden;vertical-align:middle;text-align:left;padding-left:10px;margin-left:10px;} #charactersDiv{padding:10px;overflow:auto;} #charactersDiv:hover{opacity:1;} #charactersDiv > table > thead > tr > th {font-size:16px;padding-right:10px;padding-left:10px;} #charactersDiv > table > tbody > tr > td {font-size:16px;padding-right:10px;padding-left:10px;} .tr-highlight {color:#ffffff !important;} #charactersDiv > table > tbody > tr:hover {background-color:#ffffff !important;cursor:pointer;color:#000000 !important;} #charactersDiv > table > tbody > tr:hover td:first-child { border-top-left-radius: 10px;border-bottom-left-radius: 10px;} #charactersDiv > table > tbody > tr:hover td:last-child {border-top-right-radius: 10px;border-bottom-right-radius: 10px;} `) document.querySelector("#toolbarCharacters").innerHTML = `${profileData.name}` // Toolbar alerts const raidForming = document.body.innerHTML.match('images/toolbar/NewRaid.png') const newMessage = document.body.innerHTML.match(/img border="0" src="\/images\/toolbar\/Message\.png"/i) const toolbarButtons = document.querySelector("body > center > div.sub-header-container > header > ul.navbar-item.flex-row.ml-auto") toolbarButtons.innerHTML = `
  • ` if (raidForming){ const raidFormingLi = document.createElement("li"); raidFormingLi.classList.add('toolbarButtons'); raidFormingLi.innerHTML = `` toolbarButtons.insertBefore(raidFormingLi,toolbarButtons.firstChild) }; if (newMessage){ const newMessageLi = document.createElement("li"); newMessageLi.classList.add('toolbarButtons'); newMessageLi.innerHTML = `` toolbarButtons.insertBefore(newMessageLi,toolbarButtons.firstChild) }; // Slotted augments floating pop up toolbar feature document.querySelector("#toggleAugments").addEventListener('click', async function(){ GM_addStyle(` #popUpSlottedAugsDiv > img {height:40px;width:40px;margin:3px;border-radius:8px;border:2px #475254 SOLID;} `) const augsArray = []; const equipment = await superfetch("equipment"); const itemIdArray = equipment.match(/event,'[0-9]+'/g); const itemLoop = async (item) => { const itemId = item.match(/[0-9]+/i); const itemData = await superfetchItem(itemId); const augs = itemData.augs; if (augs != ""){ for (let aug of augs.trim().split(/(?= html + element + ((index + 1) % 6 === 0 ? '
    ' : ''), ''); createWindow("Slotted Augments", "slotted_augments", 276, 276, -200); document.querySelector("#slotted_augments_content").innerHTML = `
    loading
    `; document.querySelector("#popUpSlottedAugsDiv").innerHTML = augsString }); // Add toolbar event listeners document.querySelector("#btnTravel").addEventListener("click", async () => { travelMenu(rgaName,charId,serverNo,profileData) } ); document.querySelector("#btnApps").addEventListener("click", async () => { appsMenu(rgaName,charId,serverNo,profileData) } ); document.querySelector("#btnCaster").addEventListener("click", async () => { casterMenu(rgaName,charId,serverNo,profileData) } ); document.querySelector("#btnSearch").addEventListener("click", async () => { searchMenu(rgaName,charId,serverNo) }); // Create div in bottom-left corner for robot icons const robotDiv = document.createElement("div"); robotDiv.style.position = "fixed"; robotDiv.style.bottom = '8px'; robotDiv.style.left = '8px'; robotDiv.style.cursor = 'pointer'; robotDiv.style.zIndex = "101"; robotDiv.style.alignItems = "center"; robotDiv.innerHTML = `
    ` document.body.appendChild(robotDiv); // Create alert tile toolbar div if (!url.match("home") || url.match("crew_home")) { var toolbarDiv = document.createElement("div"); toolbarDiv.id = "alertBar" toolbarDiv.style.width = "100%"; toolbarDiv.style.height = "34px"; toolbarDiv.style.position = "fixed"; toolbarDiv.style.bottom = "0"; toolbarDiv.style.backgroundColor = "#1A1C2D"; toolbarDiv.style.zIndex = "100"; toolbarDiv.style.boxShadow = "0 -5px 5px 0 rgba(0,0,0,1)"; // Add box-shadow toolbarDiv.classList.add('navbar') toolbarDiv.style.display = "flex"; toolbarDiv.style.justifyContent = "center"; toolbarDiv.style.alignItems = "center"; toolbarDiv.innerHTML = `` // Inject alert tiles to bottom toolbar document.body.appendChild(toolbarDiv); }; // Display the toolbar once it's been built GM_addStyle (`body > center > div.sub-header-container{display:revert !important;}`) }; // Points table async function toolbarPointsIcon(toolbarData){ const fetchTokens = await challengeStatus(); const qbp = await superfetch('ajax/backpackcontents.php?tab=quest'); const dataGold = parseFloat(toolbarData.replace(/,/g, '').match(/Gold:<\/b><\/td>(.*)<\/td><\/tr>/i)[1]); const dataPoints = parseFloat(toolbarData.replace(/,/g, '').match(/Points:<\/b><\/td>(.*)<\/font><\/td><\/tr>/i)[1]); const dataSkillPoints = parseFloat(toolbarData.replace(/,/g, '').match(/Skill:<\/b><\/td>(.*)<\/td><\/tr>/i)[1]); const dataPremium = parseFloat(toolbarData.replace(/,/g, '').match(/Premium:<\/b><\/td>.*?\.<\/font>[0-9]+<\/font>/i).toString().replace("","").replace("Premium:","").replace("","")); const dataFreeTokens = fetchTokens.match('error') ? "NA" : parseInt(fetchTokens.match(/"tokens":"(.*?)"/i)[1]); const dataPremiumTokens = fetchTokens.match('error') ? "NA" : parseInt(fetchTokens.match(/"p_tokens":"(.*?)"/i)[1]); const dataReps = qbp.match(/data-name="Badge Reputation"/i) ? parseInt(qbp.match(/data-name="Badge Reputation" data-itemqty="([0-9]+)"/i)[1]) : 0; const table = `
    PB POINTS:${dataPoints.toLocaleString()}
    PREM POINTS:${dataPremium.toLocaleString()}
    FREE TOKENS:${dataFreeTokens.toLocaleString()}
    PREM TOKENS:${dataPremiumTokens.toLocaleString()}
    GOLD:${dataGold.toLocaleString()}
    SKILL POINTS:${dataSkillPoints.toLocaleString()}
    BADGE REPS:${dataReps}
    ` document.querySelector("#toolbarPoints").setAttribute('onmouseover',`statspopup(event,'${table}')`); // Check if free tokens are capped if (dataFreeTokens == 30){ const toolbarLink = document.querySelector("#toolbarPoints a"); toolbarLink.style.transition = "box-shadow 1.5s ease-in-out"; toolbarLink.style.boxShadow = "inset 0 0 0 0px #F9E400"; setTimeout(() => { toolbarLink.style.boxShadow = "inset 0 0 0 1px #F9E400"; }, 100); }; // Check for 15+ badge reps if (dataReps >= 15){ const toolbarLink = document.querySelector("#toolbarPoints a"); toolbarLink.style.transition = "box-shadow 1.5s ease-in-out"; toolbarLink.style.boxShadow = "inset 0 0 0 0px #F9E400"; setTimeout(() => { toolbarLink.style.boxShadow = "inset 0 0 0 1px #00FF00"; }, 100); }; } // Character navigation function async function charactersNavigation(){ // New character dropdown const myAccount = await superfetch('myaccount') document.querySelector("#btnCharacters").addEventListener('click', async () => { await characterMenu(myAccount); }); // Next character button const currentChar = document.querySelector("#btnCharacters").innerHTML const stickyPath = window.location.pathname; const parseData = new DOMParser(); const myAccountDocument = parseData.parseFromString(myAccount, 'text/html'); let trElements = myAccountDocument.querySelectorAll("tr"); const charsArray = []; for (const tr of trElements){ const html = tr.innerHTML if (html.match(/suid=[0-9]+/i)){ const id = html.match(/suid=([0-9]+)/i)[1]; const tds = html.match(//g) const name = tds[1].replace(/<.*?>/g,''); const trustee = tds[4].match('Remove selected Trustee') ? true : false; if (!trustee){ charsArray.push({ name: name, id: id }); }; }; }; charsArray.sort((a, b) => a.name.localeCompare(b.name)); const index = charsArray.findIndex(obj => obj.name.trim() === currentChar.trim()); const nextCharacter = charsArray[(index + 1) % charsArray.length]; const nextCharacterId = nextCharacter.id; const nextCharacterName = nextCharacter.name; const toolbarNextBtn = document.querySelector("#toolbarNextBtn"); toolbarNextBtn.setAttribute("onmouseover", `statspopup(event,'Switch to ${nextCharacterName}')`); toolbarNextBtn.addEventListener('click', async () => { window.location = `${stickyPath}?suid=${nextCharacterId}`; }); const lastCharacter = charsArray[(index - 1 + charsArray.length) % charsArray.length]; const lastCharacterId = lastCharacter.id; const lastCharacterName = lastCharacter.name; const toolbarLastBtn = document.querySelector("#toolbarLastBtn"); toolbarLastBtn.setAttribute("onmouseover", `statspopup(event,'Switch to ${lastCharacterName}')`); toolbarLastBtn.addEventListener('click', async () => { window.location = `${stickyPath}?suid=${lastCharacterId}`; }); } // Character Menu async function characterMenu(myAccount) { // Toggle const menu = document.querySelector("#btnCharacters"); if (!menu.classList.contains("menu-created")) { await charactersMenuBuild(myAccount); menu.classList.add("menu-created"); } if (menu.classList.contains("menu-open")) { await charactersMenuClose(); } else { await charactersMenuOpen(); menu.classList.add("menu-open"); }; }; async function charactersMenuOpen(){ // Slider animation GM_addStyle (`#charactersDiv {animation: moveAnimationChars 0.25s ease forwards;position:fixed;display:flex;flex-direction:column;}`); GM_addStyle (`@keyframes moveAnimationChars {0% {height:0px;position:fixed;top:0px;} 100% {height:535px;position:fixed;top:48px;}}`); document.querySelector("#filter").select(); }; async function charactersMenuBuild(myAccount){ const currentChar = document.querySelector("#btnCharacters").innerHTML; const stickyPath = window.location.pathname; const parseData = new DOMParser(); const myAccountDocument = parseData.parseFromString(myAccount, 'text/html'); let trElements = myAccountDocument.querySelectorAll("tr"); const charsArray = []; const trusteesArray = []; for (const tr of trElements){ const html = tr.innerHTML if (html.match(/suid=[0-9]+/i)){ const id = html.match(/suid=([0-9]+)/i)[1]; const tds = html.match(//g) const name = tds[1].replace(/<.*?>/g,''); const level = tds[2].replace(/<.*?>/g,''); const crew = tds[3].replace(/<.*?>/g,''); const trustee = tds[4].match('Remove selected Trustee') ? true : false; const currentCharStyling = name.toLowerCase().trim() == currentChar.toLowerCase().trim() ? "tr-highlight" : "none" if (trustee){ trusteesArray.push(`${name}${level}${crew}`); } else { const power = tds[6].replace(/<.*?>/g,''); const ele = parseInt(tds[7].replace(/,/g,'').match(/>([0-9]+)${name}${level}${power}${ele.toLocaleString()}${crew}`); }; }; }; // Create div var image = document.querySelector("#btnCharacters").getBoundingClientRect(); var newDiv = document.createElement('div'); newDiv.id = 'charactersDiv'; newDiv.classList.toggle('widget'); newDiv.style.position = 'absolute'; newDiv.style.zIndex = 53; newDiv.style.boxShadow = "10px 10px 10px rgba(0, 0, 0, 1)" newDiv.style.top = image.top + window.scrollY + 'px'; newDiv.style.left = image.left + window.scrollX-0 + 'px'; document.body.appendChild(newDiv); async function loadCharacters(){ document.querySelector("#charactersDiv").innerHTML = `
    TRUSTEES
    ${charsArray.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' })).join('')}
    CHARACTERLVLPOWERELECREW

    ` document.querySelector("#btnTrustees").addEventListener('click',loadTrustees); document.querySelector("#filter").select(); await attachTableRowLinks(); await enterKeyShortcut(); await filterTables(true); await sortableTables(); }; async function loadTrustees(){ document.querySelector("#charactersDiv").innerHTML = `

    RGA
    ${trusteesArray.sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' })).join('')}
    CHARACTERLVLCREW

    ` document.querySelector("#btnMyCharacters").addEventListener('click',loadCharacters); document.querySelector("#filter").select(); await attachTableRowLinks(); await enterKeyShortcut(); await filterTables(true); await sortableTables(); }; await loadCharacters(); // Char table links async function attachTableRowLinks(){ document.querySelectorAll('tr.tr-link').forEach(tr => { tr.addEventListener('click', async () => { window.location.href = tr.id; }) }); }; // Char table filter box [return] shortcut async function enterKeyShortcut(){ document.querySelector("#filter").addEventListener("keypress", function(event) { document.querySelector("#underTableSpan").innerHTML = '

    Type [enter] to go to the character at the top of the list' if (event.key === "Enter") { const tr = document.querySelector("#characterDropdownTable tbody tr:not([style*='display: none'])"); if (tr){ window.location.href = tr.id; }; }; }); }; }; // Closes the characters menu async function charactersMenuClose(){ const menu = document.querySelector("#btnCharacters"); menu.classList.remove("menu-open"); GM_addStyle (`#charactersDiv {animation: rewindTravel 0.25s ease forwards;}`) GM_addStyle (`@keyframes rewindTravel {0% {height:535px;position:fixed;top:48px;} 100% {height:0px;position:fixed;top:0px;}}`) } // All alert tiles async function tiles(){ GM_addStyle(` div.alert-tile-bar { display:inline-block; padding-left:3px !important; padding-right:3px !important; padding-bottom:5px !important; padding-top:6px !important; border:3px SOLID #19191A; text-align:center; font-size:11px; border-top-width:3px !important; margin-right:1px; margin-left:1px; height:46px; border-top-left-radius:10px !important; border-top-right-radius:10px !important; border-bottom-left-radius:0px !important; border-bottom-right-radius:0px !important; font-family:monospace; animation: fadeIn 1s ease-in-out forwards; box-shadow: 0 0 5px rgba(0, 0, 0, 1), 0 0 10px rgba(0, 0, 0, 1), 0 0 15px rgba(0, 0, 0, 1); bottom: 24px; transition:transform 0.4s ease-in-out; } div.alert-tile-bar:hover { transform: scale(2.5) translateY(-8px); z-index: 9999; } `) // Fetch responses const endpoints = ['myaccount','gladiator','primegods','crew_bossspawns','supplies','event?eventid=top','event?eventid=woz']; const responses = await Promise.all(endpoints.map(endpoint => superfetch(endpoint))); // Return if logged in via trustee if (responses[1] == "error"){ return; }; // Create tiles array const tileEvents = []; const tileGods = []; const tileBosses = []; const tileGlad = []; const now = Math.round(new Date().getTime()) // RGA trade tile const rgaTrade = responses[0].match(/Trade/i) if (rgaTrade){ tileEvents.push(``) }; const crewTrade = responses[0].match(/a href="\/trade\?isCrewTrade=1"/i) if (crewTrade){ tileEvents.push(``) }; // Gladiator tile const parseData = new DOMParser(); const gladiator = parseData.parseFromString(responses[1], 'text/html'); const divs = gladiator.querySelectorAll('.divQuest'); for (var g = 0; g < divs.length; g++){ const divContent = divs[g].innerHTML.replace(/[\n\r]/g,''); if (divContent.match('Will retreat in')){ const gladName = divContent.match(/event,'Find (.*?)!'/i)[1]; const gladImg = divContent.match(/src="([^"]*)"/i)[1]; const gladInfo = divContent.match(/<\/div>
    <\/a>/i) const gladLink = divContent.match(//i)[1] const godHtmlId = gladName.replace(/ /g,'').replace(/,/g,'') const retreatsIn = parseInt(divContent.match(/countdown = ([0-9]+)/i)[1])*1000 const timeRemaining = Math.ceil((retreatsIn-now)/(3.6e+6)) let classEffect; if (timeRemaining <= 24){ classEffect = "tile-animate" } else { classEffect = "tile-still" }; tileGlad.push(``) }; }; // God spawn tile const spawnedGodsArray = []; const primeParser = new DOMParser(); const primeGods = primeParser.parseFromString(responses[2], 'text/html'); const mobBox = primeGods.querySelectorAll('span.mobbox:not(.grey)'); if (mobBox) { for (var i = 0; i < mobBox.length; i++){ const mob = mobBox[i] const link = mob.innerHTML.match(/primegods\?mobid=[0-9]+/i); const name = mob.innerHTML.match(/event,'([^']*)'/i)[1]; const img = mob.innerHTML.match(/src="([^"]*)"/i)[1]; spawnedGodsArray.push(name); const time = ((parseFloat(mob.innerHTML.match(/style="width: (.*?)%"/i)[1]))/100*23).toFixed(1); let classEffect; if (time <= 2){ classEffect = "tile-animate" } else { classEffect = "tile-still" }; tileGods.push(``) }; }; // PVP Brawl tile if ((GM_getValue("untilbrawl") == undefined && GM_getValue("brawlends") == undefined) || (GM_getValue("untilbrawl") <= now || GM_getValue("brawlends") <= now)){ const closedPvp = await superfetch('closedpvp'); const countdownBrawl = parseInt(closedPvp.match(/var countdown = ([0-9]+)/i)[1])*1000 if (closedPvp.match('Brawl ends in')){ GM_setValue("brawlends",countdownBrawl); GM_deleteValue("untilbrawl"); } else { GM_setValue("untilbrawl",countdownBrawl); GM_deleteValue("brawlends"); }; }; let brawlMsg; let brawlTimer; let brawlStyle; if (GM_getValue("untilbrawl") >= now){ brawlMsg = "Brawl starts in" brawlTimer = GM_getValue("untilbrawl"); brawlStyle = 'filter:grayscale(100%);' } else { brawlMsg = "Brawl ends in" brawlTimer = GM_getValue("brawlends"); brawlStyle = '' }; const differenceBrawl = (brawlTimer-now)/(3.6e+6) if (differenceBrawl <= 72){ tileEvents.push(``) tileEvents.push(``) }; // Server boss tile const aliveBosses = responses[3].replace(/[\n\r]/g,'').match(/a href="formraid\.php\?target=[0-9]+".*?/g); if (aliveBosses){ for (let i = 0; i <= aliveBosses.length-1; i++) { const boss = aliveBosses[i] const bossName = boss.match(/(.*?)<\/h3>/i)[1]; const bossHealth = boss.replace(/\s+/g,'').match(/([0-9]+)%/i)[1]; const bossImg = boss.match(/src="([^"]*)"/i)[1] const bossId = boss.match(/boss_stats\.php\?spawnid=([0-9]+)/i)[1] let classEffect; if (bossHealth <= 10){ classEffect = "tile-animate" } else { classEffect = "tile-still" }; tileBosses.push(``) }; }; // Supplies tile const supplies = parseInt(responses[4].replace(/\s/g,'').match(/([0-9]+)%<\/td>/i)[1]); if (supplies < 100){ tileEvents.push(``) }; // Strength tile const strength = parseInt(document.body.innerHTML.match(/'Strength: ([0-9]+)'/i)[1]) if (strength < 100){ tileEvents.push(``) }; // TOP tile if (responses[5].match(/var countdown = ([0-9]+)/i)){ const countdownTOP = parseInt(responses[5].match(/var countdown = ([0-9]+)/i)[1])*1000 const differenceTOP = (countdownTOP-now)/(3.6e+6) if (differenceTOP <= 336){ tileEvents.push(``) }; }; // WOZ tile if (responses[6].match(/var countdown = ([0-9]+)/i)){ const countdownWOZ = parseInt(responses[6].match(/var countdown = ([0-9]+)/i)[1])*1000 const differenceWOZ = (countdownWOZ-now)/(3.6e+6) if (differenceWOZ <= 336){ tileEvents.push(``) }; }; // Crew vault tile //if (responses[7].match(/Currently Storing [0-9]+ \/ [0-9]+<\/b> Items/i)){ //const currentlyStoring = responses[7].match(/Currently Storing ([0-9]+) \/ ([0-9]+)<\/b> Items/i); //const current = parseInt(currentlyStoring[1]); //const capacity = parseInt(currentlyStoring[2]); //if (capacity <= current){ //tileEvents.push(``) //}; //}; // Wait until toolbarTiles element is found and then load tile images - Skip if on homepage var checkElementInterval = setInterval(function() { var targetElement = document.querySelector("body > center > div.sub-header-container > header"); if (targetElement) { // Build all tiles array const tilesAll = []; if (tileEvents.length > 0){ tilesAll.push(`
    ${tileEvents.join('')}
    `); } if (tileGods.length > 0){ tilesAll.push(`
    ${tileGods.join('')}
    `); } if (tileBosses.length > 0){ tilesAll.push(`
    ${tileBosses.join('')}
    `); } if (tileGlad.length > 0){ tilesAll.push(`
    ${tileGlad.join('')}
    `); } // Inject tiles if not on the homepage if ((!url.match("home") || url.match("crew_home"))) { document.querySelector("#toolbarTiles").innerHTML = tilesAll.join('') }; // Add max supplies event listener if the tile is present if (document.querySelector("#maxSupplies")){ document.querySelector("#maxSupplies").addEventListener("click", maxSupplies); }; // Stop waiting clearInterval(checkElementInterval); }; }, 500); // If on the homepage, add tiles to the homepage if (url.match("home") && !url.match("crew_home") && !url.match("homepost")) { document.querySelector("#homeTilesContainer").innerHTML = `${tileEvents.join('')}${tileBosses.join('')}${tileGlad.join('')}${tileGods.join('')}` }; }; // Max supplies function async function maxSupplies(){ fetch('supplies', { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: new URLSearchParams({'buymax': 'Buy Max',})}) document.querySelector("#maxSupplies").remove(); const tileHealthDiv = document.querySelector("#tileHealthDiv"); if (tileHealthDiv){ if (!tileHealthDiv.innerHTML.match('tile')){ tileHealthDiv.remove(); }; } const homepageTiles = document.querySelector("#homeTilesContainer"); if (!homepageTiles){ if (document.querySelectorAll("#tilesDivEvents img").length == 0){ document.querySelector("#tilesDivEvents").remove(); }; }; }; // Travel menu toggles async function travelMenu(rgaName,charId,serverNo,profileData) { casterMenuClose(); appsMenuClose(); searchMenuClose(); // Toggle const menu = document.querySelector("#btnTravel"); if (!menu.classList.contains("menu-created")) { await travelMenuBuild(profileData); menu.classList.add("menu-created"); } if (menu.classList.contains("menu-open")) { await travelMenuClose(); } else { await travelMenuOpen(rgaName,charId,serverNo); menu.classList.add("menu-open"); menu.classList.remove("menu-close"); }; }; async function travelMenuOpen(rgaName,charId,serverNo){ const server = serverNo == "1" ? "sigil" : "torax"; // Rotate fast travel image document.querySelector("#btnTravel > img").classList.toggle('rotate180'); GM_addStyle (`#btnTravel > img {transition: transform 0.5s ease;}`) GM_addStyle (`.rotate180 {transform: rotate(180deg);}`) // No Moxximod+ subscription modifications if (!GM_getValue("auth").match("Full")){ GM_addStyle (`a.plus-destination{opacity:0.25;}`) const plusOnly = document.querySelectorAll("a.plus-destination") for (let i = 0; i < plusOnly.length; i++) { plusOnly[i].innerHTML = `MM+ REQUIRED` }; }; // Slider animation GM_addStyle (`#travelDiv {animation: moveAnimation 0.5s ease forwards;position:fixed;}`) GM_addStyle (`@keyframes moveAnimation {0% {width:370px;height:0px;position:fixed;top:0px;} 100% {width:370px;height:535px;position:fixed;top:45px;}}`) // All travel button functions document.querySelector("#travelRoomNum").addEventListener("keyup", async function(event) { if (event.keyCode === 13) { const roomNum = document.querySelector("#travelRoomNum").value await goToRoomNum(server,charId,roomNum) }; }); // Button functions const travelTeleporterArray = document.querySelectorAll(".travel-teleporter"); for (let i = 0; i < travelTeleporterArray.length; i++) { var teleporter = travelTeleporterArray[i]; const keyName = teleporter.innerHTML.match(/alt="([^"]*)"/i)[1]; teleporter.addEventListener("click", async function() { await activateKeyFromBp(server,rgaName,charId,serverNo,keyName); window.location.href = "world" }); }; const plusDestinationArray = document.querySelectorAll(".plus-destination"); for (let i = 1; i < plusDestinationArray.length; i++) { var room = plusDestinationArray[i]; const roomNum = room.innerHTML.match(/alt="([^"]*)"/i)[1]; room.addEventListener("click", async function() { await goToRoomNum(server,charId,roomNum); }); }; } async function travelMenuBuild(profileData){ // Build travel menu const fastTravelHtml = ` 36195AEZEL, GARDEN MANIC 1066AIYUKEN Astral WardASTRAL RIFT Astral TeleporterASTRAL RUINS CHALLENGE ARENA 32877CHAOS GOLEMS Sanctum KeyCITY SANCTUM 26201ELEMENTAL OVERLORD ELITE ANTAGONIST 50GLADIATOR ARENA 28040INFINITE TOWER INTERSECTION ROOM 11 PRECISION ADVERSARY 28123PRISON: HOVOK Grove InsigniaTWILIGHT GROVE Triworld TeleporterTRIWORLD SANCTUARY 42550VAULT OF MADNESS Veiled TeleporterVEILED PASSAGE Veldara TeleporterVELDARA GARRISON ` // Create div var image = document.querySelector("#btnTravel").getBoundingClientRect(); var newDiv = document.createElement('div'); newDiv.id = 'travelDiv'; newDiv.classList.toggle('widget'); newDiv.innerHTML = fastTravelHtml; newDiv.style.position = 'absolute'; newDiv.style.top = image.top + window.scrollY + 'px'; newDiv.style.left = image.left + window.scrollX-320 + 'px'; document.body.appendChild(newDiv); // Faction-specific locations if (profileData.faction == "Vordyn"){ document.querySelector("#travelEliteAntogonist").setAttribute('alt', '43049'); document.querySelector("#travelEliteAntogonist").setAttribute('src', 'images/mobs/owmob129.png'); document.querySelector("#travelPrecisionAdversary").setAttribute('alt', '43500'); document.querySelector("#travelPrecisionAdversary").setAttribute('src', 'images/mobs/owmob1129.png'); } else if (profileData.faction == "Alvar"){ document.querySelector("#travelEliteAntogonist").setAttribute('alt', '43173'); document.querySelector("#travelEliteAntogonist").setAttribute('src', 'images/mobs/owmob1058.png'); document.querySelector("#travelPrecisionAdversary").setAttribute('alt', '43504'); document.querySelector("#travelPrecisionAdversary").setAttribute('src', 'images/mobs/owmob784.png'); } else if (profileData.faction == "Delruk"){ document.querySelector("#travelEliteAntogonist").setAttribute('alt', '42903'); document.querySelector("#travelEliteAntogonist").setAttribute('src', 'images/mobs/owmob1050.png'); document.querySelector("#travelPrecisionAdversary").setAttribute('alt', '43502'); document.querySelector("#travelPrecisionAdversary").setAttribute('src', 'images/mobs/owmob176.png'); } else { document.querySelector("#travelEliteAntogonist").parentElement.remove(); document.querySelector("#travelPrecisionAdversary").parentElement.remove(); } }; // Go to room number (moxximod+) async function goToRoomNum(server,charId,roomNum){ await loadingOverlay(); await travelMenuClose(); const send = await mmplus(`GoToRm|rganame|${server}|${charId}|${roomNum}`); if (send.match('Full')){ // Send data to server to move // Loop through room ajax page until arrived at destination async function endlessLoop() { const data = await superfetch('ajax_changeroomb.php',true); if (data.match(/"curRoom":"([0-9]+)"/i)[1] == roomNum) { window.location.href = "world" return; }; setTimeout(endlessLoop, 250); }; endlessLoop(); } else { await loadingOff(); }; }; // Activate key from backpack async function activateKeyFromBp(server,rgaName,charId,serverNo,keyName){ const kbp = await superfetch(`https://${server}.outwar.com/ajax/backpackcontents.php?rg_sess_id=${rgaName}&suid=${charId}&serverid=${serverNo}&tab=key`); if (!kbp.match(keyName)){ alert('ERROR: You do not have the teleporter needed') } else { var regex = new RegExp(`data-name="${keyName}".*data-iid="([0-9]+)"`, 'i'); const teleporter = new URLSearchParams({ 'action': 'activate', 'itemids[]': kbp.match(regex)[1] }); await superpost(`https://${server}.outwar.com/ajax/backpack_action.php?rg_sess_id=${rgaName}&suid=${charId}&serverid=${serverNo}`,teleporter.toString()); }; }; // Apps menu toggles async function appsMenu(rgaName,charId,serverNo,profileData) { travelMenuClose(); casterMenuClose(); searchMenuClose(); // Toggle const menu = document.querySelector("#btnApps"); if (!menu.classList.contains("menu-created")) { await appsMenuBuild(rgaName,charId,serverNo); menu.classList.add("menu-created"); } if (menu.classList.contains("menu-open")) { await appsMenuClose(); } else { await appsMenuOpen(rgaName,charId,serverNo); menu.classList.add("menu-open"); menu.classList.remove("menu-close"); }; }; // Opens the apps menu async function appsMenuOpen(rgaName,charId,serverNo) { // Rotate moxximod apps image document.querySelector("#btnApps > img").classList.toggle('rotate180'); GM_addStyle (`#btnApps > img {transition: transform 0.5s ease;}`) GM_addStyle (`.rotate180 {transform: rotate(180deg);}`) // Slider animation GM_addStyle (`#appsDiv {animation: moveAnimation 0.5s ease forwards;position:fixed;}`) GM_addStyle (`@keyframes moveAnimation {0% {width:535px;height:0px;position:fixed;top:0px;} 100% {width:535px;height:535px;position:fixed;top:45px;}}`) }; async function appsMenuBuild(rgaName,charId,serverNo){ const server = serverNo == "1" ? "sigil" : "torax"; const serverOther = server == "sigil" ? "torax" : "sigil"; const serverOtherNo = server == "sigil" ? "2" : "1"; // Build moxximod apps menu const appsHtml = `

    AUTHENTICATE

    BACKPACKS

    BADGE REPORT

    CHAOS REPORT

    CHAOS TELES

    DISCORD

    ENHANCER REPORT

    EXCHANGE RATES

    EXPORT RGA

    GLADIATORS

    ILLUSIONS

    INVENTORY

    ITEM UPGRADES

    LING REPORT

    MOB ATTACKER

    MOB RAIDER

    MOXXIVISION

    OUTWAR THEME

    PRIME RAIDER

    RAIDS REPORT

    RUNE REPORT

    SAVED RGAS

    X

    UNFINISHED QUESTS

    WILDERNESS

    ` // Create div var image = document.querySelector("#btnApps").getBoundingClientRect(); var newDiv = document.createElement('div'); newDiv.id = 'appsDiv'; newDiv.classList.toggle('widget'); newDiv.innerHTML = appsHtml; newDiv.style.position = 'absolute'; newDiv.style.top = image.top + window.scrollY + 'px'; newDiv.style.left = image.left + window.scrollX-485 + 'px'; document.body.appendChild(newDiv); // Auth-conditional functions if (!GM_getValue("auth").match("Full")){ GM_addStyle (`div.plus-app{opacity:0.25;}`) }; // Switch server button document.querySelector("#serverSwitchSpan").innerHTML = "TO " + serverOther.toUpperCase(); document.querySelector("#serverSwitch").addEventListener('click', async function(){ const ajax = await superfetch(`ajax/accounts.php?t_serv=${serverOtherNo}`); const id = (ajax.match(/"id":"([0-9]+)"/i) || ['0','0'])[1]; if (id == '0'){ alert(`ERROR: No ${serverOther} chars found`); } else { window.location.href = `https://${serverOther}.outwar.com/world?suid=${id}&serverid=${serverOtherNo}` }; }); // All button functions document.querySelector("#authApp").addEventListener('click',async function(){ await appsMenuClose(); await mmplus('AuthCheck|rganame'); }); document.querySelector("#exportRga").addEventListener('click',async function(){ await blankOverlay(server,serverNo,rgaName,charId); await appExportrga(server,serverNo,rgaName,charId); }); document.querySelector("#itemStorage").addEventListener('click',async function(){ await blankOverlay(server,serverNo,rgaName,charId); await appItemStorage(server,serverNo,rgaName,charId); }); document.querySelector("#illusionsApp").addEventListener('click',async function(){ await blankOverlay(server,serverNo,rgaName,charId); await appIllusions(server,serverNo,rgaName,charId); }); document.querySelector("#chaosTeles").addEventListener('click',async function(){ await blankOverlay(server,serverNo,rgaName,charId); await appChaosTeles(server); }); document.querySelector("#wildernessApp").addEventListener('click',async function(){ await blankOverlay(server,serverNo,rgaName,charId); await appWilderness(server,serverNo,rgaName,charId); }); document.querySelector("#savedRgas").addEventListener('click',async function(){ await blankOverlay(server,serverNo,rgaName,charId); await appSavedRgas(server,serverNo,rgaName,charId); }); document.querySelector("#questreportApp").addEventListener('click',async function(){ window.location.href = "treasury.php?type=questreport" }); document.querySelector("#itemAnalyzer").addEventListener('click',async function(){ await blankOverlay(server,serverNo,rgaName,charId); await appItemAnalyzer(server,serverNo,rgaName,charId); }); document.querySelector("#mobAttacker").addEventListener('click',async function(){ await blankOverlay(server,serverNo,rgaName,charId); await appMobAttacker(server,serverNo,rgaName,charId); }); document.querySelector("#mobRaider").addEventListener('click',async function(){ await blankOverlay(server,serverNo,rgaName,charId); await appMobRaider(server,serverNo,rgaName,charId); }); document.querySelector("#primeRaider").addEventListener('click',async function(){ await blankOverlay(server,serverNo,rgaName,charId); await appPrimeRaider(server,serverNo,rgaName,charId); }); document.querySelector("#gladiatorApp").addEventListener('click',async function(){ await blankOverlay(server,serverNo,rgaName,charId); await appGladiator(server,serverNo,rgaName,charId); }); document.querySelector("#raidsReport").addEventListener('click',async function(){ await blankOverlay(server,serverNo,rgaName,charId); await appRaidsReport(server,serverNo,rgaName,charId); }); document.querySelector("#backpackApp").addEventListener('click',async function(){ await blankOverlay(server,serverNo,rgaName,charId); await appBackpack(server,serverNo,rgaName,charId); }); document.querySelector("#calculatorApp").addEventListener('click',async function(){ await blankOverlay(server,serverNo,rgaName,charId); await appCalculator(server,serverNo,rgaName,charId); }); document.querySelector("#moxxiVision").addEventListener('click',async function(){ window.location.href = "treasury.php?type=vision" }); document.querySelector("#themeApp").addEventListener('click',async function(){ await appCustomTheme(); }); document.querySelector("#reportBadge").addEventListener('click',async function(){ await appsMenuClose(); await mmplus(`BadgeNeeds|rganame|${server}`); }); document.querySelector("#reportChaos").addEventListener('click',async function(){ await appsMenuClose(); await mmplus(`ChaosNeeds|rganame|${server}`); }); document.querySelector("#reportRune").addEventListener('click',async function(){ await appsMenuClose(); await mmplus(`RuneNeeds|rganame|${server}`); }); document.querySelector("#reportEnhancer").addEventListener('click',async function(){ await appsMenuClose(); await mmplus(`EnhanceNeeds|rganame|${server}`); }); document.querySelector("#reportLing").addEventListener('click',async function(){ await appsMenuClose(); await mmplus(`LingNeeds|rganame|${server}`); }); document.querySelector("#goToDiscord").addEventListener('click',async function(){ window.open("https://discord.com/invite/f35cccbWU8?utm_source=Discord%20Widget&utm_medium=Connect", "_blank"); }); } // App export RGA async function appExportrga(server,serverNo,rgaName,charId){ // Styling GM_addStyle(` #overlayWidget{text-align:center;padding:3rem} #exportRgaTxt{width:80%;font-size:36px;margin-top:1rem;text-align:center;height:100px;} `) // Loading document.querySelector("#overlayWidget").innerHTML = '' // Get data const chars = await superfetch(`ajax/accounts.php?t_serv=${serverNo}`); const ids = chars.match(/"id":"[0-9]+"/g).map(id => id.match(/[0-9]+/)[0]); let string = `ExportRGA|${server}|` const getRgaCharDataForExport = async (id) => { const profile = await superfetchProfile(`profile?suid=${id}`); const array = []; const name = profile.name; const level = profile.level; const exp = profile.exp; const mr = profile.maxrage; const power = profile.power; const ele = profile.elemental; const atk = profile.attack; const hp = profile.hp; const chaos = profile.chaos; const resist = profile.resist; const core = [profile.core.img,profile.core.id]; const head = [profile.head.img,profile.head.id]; const neck = [profile.neck.img,profile.neck.id]; const weapon = [profile.weapon.img,profile.weapon.id]; const body = [profile.body.img,profile.body.id]; const shield = [profile.shield.img,profile.shield.id]; const belt = [profile.belt.img,profile.belt.id]; const pants = [profile.pants.img,profile.pants.id]; const ring = [profile.ring.img,profile.ring.id]; const foot = [profile.foot.img,profile.foot.id]; const gem = [profile.gem.img,profile.gem.id]; const badge = [profile.badge.img,profile.badge.id]; const rune = [profile.rune.img,profile.rune.id]; const orb1 = [profile.orb1.img,profile.orb1.id]; const orb2 = [profile.orb2.img,profile.orb2.id]; const orb3 = [profile.orb3.img,profile.orb3.id]; array.push(name,level,exp,power,ele,resist,atk,hp,chaos,mr,core[0],core[1],head[0],head[1],neck[0],neck[1],weapon[0],weapon[1],body[0],body[1],shield[0],shield[1],belt[0],belt[1],pants[0],pants[1],ring[0],ring[1],foot[0],foot[1],gem[0],gem[1],rune[0],rune[1],orb1[0],orb1[1],orb2[0],orb2[1],orb3[0],orb3[1],badge[0],badge[1]); string += array.join(',').replace(/http:\/\/[A-Za-z]+\./g,'') + ','; }; await Promise.all(ids.map(getRgaCharDataForExport)); // Get export URL const url = await mmplus(string.slice(0,-1)); document.querySelector("#overlayWidget").innerHTML = `

    Your Exported RGA

    ` // Copy to clipboard document.querySelector("#copyExportRgaLinkToClipboard").addEventListener('click', async function(){ const url = document.querySelector("#exportRgaTxt").value; navigator.clipboard.writeText(url); alert('Copied to clipboard'); }) }; // App quest report async function appQuestReport(){ // Styling GM_addStyle(` #overlayWidget{text-align:center;} #questReportOuterDiv{display:flex;flex-direction:column;height:100%;} #questReportInnerDiv{height:100%;overflow:auto;flex-grow:1;margin-bottom:1rem;} #hiddenDiv{position:absolute;bottom:10px;} `) // Loading page document.querySelector("#overlayWidget").innerHTML = `` // Get quest data const log = await superfetch('questlog'); const acceptedQuests = (log.match(/questrow[0-9]+/g) || []).map(i => i.match(/[0-9]+/)[0]); const newQuests = (log.match(/quest=[0-9]+/g) || []).map(i => i.match(/[0-9]+/)[0]); const allQuests = [...new Set([...acceptedQuests, ...newQuests])]; const questsArray = []; const hiddenArray = []; const getQuestInfo = async (id) => { const quest = await superfetch(`show_quest.php?quest=${id}`); if (!quest.match(/Show Quest: (.*?)<\/title>/i)){ hiddenArray.push(id); return; }; // Get total exp received from all quest steps in the quest const questExp = ((quest.replace(/,/g,'').match(/received [0-9]+ experience/g) || ['0']).map(i => parseInt(i.match(/[0-9]+/)[0]))).reduce((a, b) => a + b, 0); // Get quest name const questName = quest.match(/<title>Show Quest: (.*?)<\/title>/i)[1]; // Check for quest shard requirements const questShards = ((quest.replace(/,/g,'').match(/Collect: [0-9]+ Quest Shard/g) || ['0']).map(i => parseInt(i.match(/[0-9]+/)[0]))).reduce((a, b) => a + b, 0); // Build data object questsArray.push( ` <tr> <td><input type="checkbox" class="questListCheckbox" name="${questName}"></td> <td>${questName}</td> <td>${questShards}</td> <td>${questExp.toLocaleString()}</td> </tr> ` ); }; await Promise.all(allQuests.map(getQuestInfo)); document.querySelector("#overlayWidget").innerHTML = ` <div id="questReportOuterDiv"> <div id="questReportInnerDiv"> <table class="table table-striped sortable" style="text-align:left"> <thead><tr><th></th><th>Unfinished Quest</th><th>Quest Shards Needed</th><th>Total Quest Exp</th></tr></thead> ${questsArray.join('')} </table> </div> <div style="width:auto"> <button class="btn-mm" id="makeQuestList">SEND ME A QUEST LIST OF SELECTED QUESTS</button><br> <i>This is a moxximod+ feature. Please only click once</i> </div> </div> <div id="hiddenDiv"></div> ` // Hidden quest eye if (hiddenArray.length > 0){ document.querySelector("#hiddenDiv").innerHTML = ` <i class="fa fa-eye" style="font-size:24px;cursor:pointer;" onmouseover="statspopup(event,'<b>Unable to find information on ${hiddenArray.length} incomplete quests<br>${hiddenArray.join(', ')}')" onmouseout="kill()"></b></i> ` }; // Make quest list function document.querySelector("#makeQuestList").addEventListener('click', async function(){ const selected = []; const checkboxes = document.querySelectorAll('input.questListCheckbox:checked'); checkboxes.forEach(i => { selected.push(i.outerHTML.match(/name="([^"]*)"/i)[1]); }); await mmplus(`QuestList|rganame|${selected.join(',')}`); }); await sortableTables(); }; // App calculator async function appCalculator(){ // Styling GM_addStyle(` div.item-calc-div {display: inline-block;font-size: 10px;text-align: center;font-family: monospace,monospace;margin:0.5rem;background:#000000;box-shadow:0 3px 3px 0 rgba(0,0,0,0.7);padding:0.5rem;border-radius:10px;} .item-calc-div > img {height:60px; width:60px;border-radius:10px;margin-bottom:1rem;background:#000000;} .item-txt-calc {width:80px;} #overlayWidget{text-align:center;} `); // Loading document.querySelector("#overlayWidget").innerHTML = '<img src="https://studiomoxxi.com/moxximod/loading-gif.gif" style="height:100px;width:100px">' // Array of items to grab pricing for and display as currency exchange const targetItems = [ 'Faction Change', 'Character Class Change', 'Cosmic Mote', 'Vault Tear', 'Interstellar Vessel', 'Dimensional Bond', 'Power Potion Pack', 'Bottled Chaos', 'Add Augment Slot', 'Wrapped Package', 'Remove Augment', 'Pulsating Stone', 'Thunder Ball', 'Force of Veldara', 'Remove All Augments', 'Profound Ward', 'Spark the Fury', 'Rechage Totem', 'Recharge the Fury', 'Standard Issue Neuralyzer', 'Vanishas Fragrance', 'Advanced Neuralyzer', 'Ask the Oracle: Spawn Time' ]; const vendor = await superfetch('ajax/ajax_treasury.php?search_for=Vendor') // Remove quotations from data to solve issue where PP discount prices are displayed without quotes and regular prices are displayed with quotes const allItems = vendor.replace(/"/g,'').match(/[0-9]+,[0-9]+,.*?,.*?,.*?,/g) const items = []; const parseEachItem = async (item) => { for (let i = 0; i < targetItems.length; i++) { if (item.includes(targetItems[i])) { const string = item.match(/([0-9]+),[0-9]+,(.*?),(.*?),(.*?),/i) const rollover = await superfetch(`item_rollover.php?id=${string[1]}&data=1`); const tradable = rollover.match(/\[Player Bound\]/i) ? false : true; items.push(`<div class="item-calc-div"><img src="images/${string[3].replace(/\\/g,'')}" onmouseover="statspopup(event,'<b>${string[2]}<b>')" onmouseout="kill()"><br><input type="text" alt="${string[4]}" tradable="${tradable}" class="form-control-new item-txt-calc" disabled></div>`); }; }; }; await Promise.all(allItems.map(parseEachItem)); document.querySelector("#overlayWidget").innerHTML = ` <div class="item-calc-div" style="width:280px"> <h5>PREMIUM POINTS</h5><br> <input type="text" class="form-control-new" id="calcPrem"> </div> <div class="item-calc-div" style="width:280px"> <h5>PLAYERBOUND POINTS</h5><br> <input type="text" class="form-control-new" id="calcPb"> </div> <div class="item-calc-div" style="width:280px"> <h5>TOKENS</h5><br> <input type="text" class="form-control-new" id="calcTk"> </div> <div class="item-calc-div" style="width:280px"> <h5>CASH</h5><br> <input type="text" class="form-control-new" id="calcCash"> </div> <hr> ${items.join('')} <hr> <img src="images/profile/ProPP.png" id="calcPP"> ` // Playerbound points input function document.querySelector("#calcPb").addEventListener('input', async function(){ const pnt = document.querySelector("#calcPb").value document.querySelector("#calcPrem").value = (pnt*0.8183306055646481).toFixed(2); document.querySelector("#calcTk").value = (pnt*0.225).toFixed(2); document.querySelector("#calcCash").value = (pnt*0.018).toFixed(2); await itemMath(); }); // Premium points input function document.querySelector("#calcPrem").addEventListener('input', async function(){ const pnt = document.querySelector("#calcPrem").value document.querySelector("#calcPb").value = (pnt*1.222).toFixed(2); document.querySelector("#calcTk").value = (pnt*0.275).toFixed(2); document.querySelector("#calcCash").value = (pnt*0.022).toFixed(2); await itemMath(); }); // Tokens input function document.querySelector("#calcTk").addEventListener('input', async function(){ const pnt = document.querySelector("#calcTk").value document.querySelector("#calcPrem").value = (pnt*3.635).toFixed(2); document.querySelector("#calcPb").value = (pnt*4.445).toFixed(2); document.querySelector("#calcCash").value = (pnt*0.08).toFixed(2); await itemMath(); }); // Cash input function document.querySelector("#calcCash").addEventListener('input', async function(){ const pnt = document.querySelector("#calcCash").value document.querySelector("#calcPrem").value = (pnt*45.45).toFixed(2); document.querySelector("#calcPb").value = (pnt*55.55).toFixed(2); document.querySelector("#calcTk").value = (pnt*12.5).toFixed(2); await itemMath(); }); // Math how many of each item can be purchased based on input async function itemMath(){ const allTxtBoxes = document.querySelectorAll(".item-txt-calc"); allTxtBoxes.forEach(i => { const price = parseFloat(i.outerHTML.match(/alt="([^"]*)"/i)[1]); const tradable = i.outerHTML.match(/tradable="([^"]*)"/i)[1] == "true" if (tradable){ i.value = (document.querySelector("#calcPrem").value/price).toFixed(1); } else { i.value = (document.querySelector("#calcPb").value/price).toFixed(1); }; }); }; // Check for preferred player if (document.body.innerHTML.match('Ultimate Preferred Player')){ document.querySelector("#calcPP").setAttribute('onmouseover',`statspopup(event,'<b>Item prices include preferred player discount<b>')`); document.querySelector("#calcPP").setAttribute('onmouseout',`kill()`); } else { document.querySelector("#calcPP").setAttribute('onmouseover',`statspopup(event,'<b>Item prices do not include preferred player discount<b>')`); document.querySelector("#calcPP").setAttribute('onmouseout',`kill()`); document.querySelector("#calcPP").setAttribute('style','filter: grayscale(100%);'); }; }; // App item storage async function appItemStorage(server,serverNo,rgaName,charId){ // Build HTML element document.querySelector("#overlayWidget").innerHTML = ` <div id="itemFilterDiv" style="height:5%;"></div> <div id="itemStorageDiv" style="text-align:center;height:95%;overflow:auto;"> <img src="https://studiomoxxi.com/moxximod/loading-gif.gif" style="height:100px;width:100px"> </div> ` // Get char IDs const charDropdown = document.body.innerHTML.replace(/[\n\r]/g,'').match(/<optgroup label="My Characters">.*?<option value="0">--Change Server--<\/option>/i) const charIdArray = charDropdown.toString().match(/value="[0-9]+"/g).map(match => match.match(/[0-9]+/)[0]).slice(0, -1); // Creat array to populate let array = []; // Loop through backpacks and push findings to array const inventory = async (id) => { const types = ['regular', 'quest', 'orb', 'potion', 'key']; // Build a new set to prevent duplicates from being pushed to array const encounteredNames = new Set(); for (const type of types) { const look = await superfetch(`ajax/backpackcontents.php?tab=${type}&suid=${id}`); const parse = look.match(/data-itemidqty="[0-9]+" data-name="[^"]*".*?src="[^"]*"/g); if (parse){ const items = look.match(/data-itemidqty="[0-9]+" data-name="[^"]*".*?src="[^"]*"/g).map(i => [i.match(/data-name="([^"]*)"/i)[1], i.match(/data-itemidqty="([0-9]+)"/i)[1], i.match(/src="([^"]*)"/i)[1]]); for (let i = 0; i < items.length; i++) { const [name] = items[i]; // Check to make sure the item hasn't already been pushed if (!encounteredNames.has(name)) { array.push(items[i]); encounteredNames.add(name); }; }; }; }; const lookEq = await superfetch(`equipment?suid=${id}`); const equipped = (lookEq.match(/src="[^"]*" ONMOUSEOVER="itempopup\(event,'[0-9]+'\)" ONMOUSEOUT="kill\(\)" onclick="removeItem\('[0-9]+',[0-9]+,[0-9]+\);document\.getElementById\('[^']*'\)\.innerHTML=''" alt="[^"]*"/g) || []).map(item => [item.match(/alt="([^"]*)"/i)[1], "1", item.match(/src="([^"]*)"/i)[1]]); for (let i = 0; i < equipped.length; i++) { array.push(equipped[i]); }; }; await Promise.all(charIdArray.map(inventory)); // Combine multiples of the same item found and sum quantities together let combinedData = {}; array.forEach(item => { let name = item[0]; let quantity = parseInt(item[1]); if (combinedData[name]) { combinedData[name][1] += quantity; } else { combinedData[name] = [name, quantity, item[2]]; }; }); let finalData = Object.values(combinedData); let tableData = [] for (let i = 0; i < finalData.length; i++) { const item = finalData[i] tableData.push(`<tr><td><img src="${item[2]}" style="width:25px;height:25px"></td><td>${item[0]}</td><td>${item[1]}</td></tr>`) } document.querySelector("#itemFilterDiv").innerHTML = `<input type="text" class="form-control-new mb-3" "style="font-size:15px;width:50%;" id="filterInput" placeholder="Filter...">` document.querySelector("#itemStorageDiv").innerHTML = ` <table id="inventory" class="table table-striped sortable"> <thead><tr><th>Image</th><th>Item</th><th>Qnt</th></tr></thead> <tbody>${tableData.join('')}</tbody> </table> ` // Table filter document.getElementById("filterInput").addEventListener("keyup", function() { var filter = this.value.toLowerCase(); var rows = document.querySelectorAll("#inventory tr"); rows.forEach(function(row, index) { if (index === 0) return; // Skip the header row var cells = row.getElementsByTagName("td"); var showRow = false; for (var i = 0; i < cells.length; i++) { var cell = cells[i]; if (cell.textContent.toLowerCase().indexOf(filter) > -1) { showRow = true; break; } } row.style.display = showRow ? "" : "none"; }); }); // Make table sortable sortableTables(); }; // App illusions async function appIllusions(server,serverNo,rgaName,charId){ // Styling GM_addStyle (` .form-control-new{height:30px;width:350px;margin:3px;padding-left:10px;} #search > img{height:40px;width:40px;margin:5px;border-radius:5px;border:2px #475254 SOLID;background-color:#000000;} `) // HTML document.querySelector("#overlayWidget").innerHTML = ` <center> <!-- Search boxes --> <div class="row justify-content-center"> <div class="col-xl-3 col-lg-6 col-md-6 col-sm-12 col-12 layout-spacing px-1"> <h3>Illusion Item Search</h3> <form class="form" id="myForm" action="https://torax.outwar.com/livesearchcore/AjaxProcessor.php" method="post"> <input type="text" name="slot10" class="illusion-search form-control-new" autocomplete="off" placeholder="Core"> </form> <form class="form" id="myForm" action="https://torax.outwar.com/livesearchcore/AjaxProcessor.php" method="post"> <input type="text" name="slot5" class="illusion-search form-control-new" autocomplete="off" placeholder="Head"> </form> <form class="form" id="myForm" action="https://torax.outwar.com/livesearchcore/AjaxProcessor.php" method="post"> <input type="text" name="slot6" class="illusion-search form-control-new" autocomplete="off" placeholder="Neck"> </form> <form class="form" id="myForm" action="https://torax.outwar.com/livesearchcore/AjaxProcessor.php" method="post"> <input type="text" name="slot3" class="illusion-search form-control-new" autocomplete="off" placeholder="Weapon"> </form> <form class="form" id="myForm" action="https://torax.outwar.com/livesearchcore/AjaxProcessor.php" method="post"> <input type="text" name="slot0" class="illusion-search form-control-new" autocomplete="off" placeholder="Body"> </form> <form class="form" id="myForm" action="https://torax.outwar.com/livesearchcore/AjaxProcessor.php" method="post"> <input type="text" name="slot1" class="illusion-search form-control-new" autocomplete="off" placeholder="Shield"> </form> <form class="form" id="myForm" action="https://torax.outwar.com/livesearchcore/AjaxProcessor.php" method="post"> <input type="text" name="slot7" class="illusion-search form-control-new" autocomplete="off" placeholder="Belt"> </form> <form class="form" id="myForm" action="https://torax.outwar.com/livesearchcore/AjaxProcessor.php" method="post"> <input type="text" name="slot9" class="illusion-search form-control-new" autocomplete="off" placeholder="Pants"> </form> <form class="form" id="myForm" action="https://torax.outwar.com/livesearchcore/AjaxProcessor.php" method="post"> <input type="text" name="slot4" class="illusion-search form-control-new" autocomplete="off" placeholder="Ring"> </form> <form class="form" id="myForm" action="https://torax.outwar.com/livesearchcore/AjaxProcessor.php" method="post"> <input type="text" name="slot2" class="illusion-search form-control-new" autocomplete="off" placeholder="Foot"> </form> </div> <!-- The dude --> <div class="col-xl-3 col-lg-6 col-md-6 col-sm-12 col-12 layout-spacing px-1""> <div style="position:relative; width:300px; height:385px; background-image:url(/images/thedude.png)" id="thedude"> <div id="slot10" class="illusion-slot" style="position:absolute; left:61px; top:12px; width:41px; height:41px;text-align:center"></div> <div id="slot5" class="illusion-slot" style="position:absolute; left:118px; top:7px; width:62px; height:46px;text-align:center"></div> <div id="slot6" class="illusion-slot" style="position:absolute; left:197px; top:12px; width:41px; height:41px;text-align:center"></div> <div id="slot3" class="illusion-slot" style="position:absolute; left:45px; top:67px; width:56px; height:96px;text-align:center"></div> <div id="slot0" class="illusion-slot" style="position:absolute; left:121px; top:67px; width:56px; height:96px;text-align:center"></div> <div id="slot1" class="illusion-slot" style="position:absolute; left:198px; top:67px; width:56px; height:96px;text-align:center"></div> <div id="slot9" class="illusion-slot" style="position:absolute; left:118px; top:175px; width:62px; height:75px;text-align:center"></div> <div id="slot7" class="illusion-slot" style="position:absolute; left:61px; top:192px; width:41px; height:41px;text-align:center"></div> <div id="slot4" class="illusion-slot" style="position:absolute; left:197px; top:192px; width:41px; height:41px;text-align:center"></div> <div id="slot2" class="illusion-slot" style="position:absolute; left:118px; top:262px; width:62px; height:66px;text-align:center"></div> </div> Click item to remove<br> </div> <!-- List of items --> <div class="col-xl-3 col-lg-6 col-md-6 col-sm-12 col-12 layout-spacing px-1" style="text-align:left;"> <div class="list-group text-left"> <div class="list-group-item">CORE: <span id="txtslot10"></span></div> <div class="list-group-item">HEAD: <span id="txtslot5"></span></div> <div class="list-group-item">NECK: <span id="txtslot6"></span></div> <div class="list-group-item">WEAPON: <span id="txtslot3"></span></div> <div class="list-group-item">BODY: <span id="txtslot0"></span></div> <div class="list-group-item">SHIELD: <span id="txtslot1"></span></div> <div class="list-group-item">BELT: <span id="txtslot7"></span></div> <div class="list-group-item">PANTS: <span id="txtslot9"></span></div> <div class="list-group-item">RING: <span id="txtslot4"></span></div> <div class="list-group-item">FOOT: <span id="txtslot2"></span></div> </div> </div> </div> <!-- Item search results --> <div id="search"></div> ` // Get equipped items const eq = await superfetch('equipment?r=0'); const items = eq.match(/src="[^"]*" ONMOUSEOVER="itempopup\(event,'[0-9]+'\)" ONMOUSEOUT="kill\(\)" onclick="removeItem\('[0-9]+',[0-9]+,[0-9]+\);document\.getElementById\('slot[0-9]+'\)/g); const slots = items.map(i => i.match(/slot[0-9]+/)[0]); for (let i = 0; i < slots.length; i++) { const slot = slots[i] const dude = document.querySelector(`#${slot}`) if (dude){ const regex = new RegExp(`src="[^"]*" ONMOUSEOVER="itempopup\\(event,'[0-9]+'\\)" ONMOUSEOUT="kill\\(\\)" onclick="removeItem\\('[0-9]+',[0-9]+,[0-9]+\\);document\.getElementById\\('${slot}'\\)`); const img = eq.match(regex).toString().match(/src="([^"]*)"/i)[1]; const iid = eq.match(regex).toString().match(/event,'([0-9]+)'/i)[1]; dude.setAttribute('alt',iid) dude.innerHTML = `<img src="${img}">` }; }; // Click to remove item from the dude const dudeSlot = document.querySelectorAll(".illusion-slot"); for (let i = 0; i < dudeSlot.length; i++) { dudeSlot[i].addEventListener('click', function(){ const slot = dudeSlot[i].outerHTML.match(/id="([^"]*)"/i)[1]; document.querySelector(`#txt${slot}`).innerHTML = ''; dudeSlot[i].innerHTML = '' }); }; // Illusion item searches const search = document.querySelectorAll(".illusion-search"); for (let i = 0; i < search.length; i++) { search[i].addEventListener('input', async function(){ const value = search[i].value; const html = search[i].outerHTML const slot = html.match(/name="([^"]*)"/i)[1] const dude = document.querySelector(`#${slot}`).outerHTML const iid = dude.match(/alt="([^"]*)"/i)[1] const post = await fetch('livesearchcore/AjaxProcessor.php', { method: 'POST', headers: { "content-type": "application/x-www-form-urlencoded; charset=UTF-8","x-requested-with": "XMLHttpRequest" }, body: `ls_anti_bot=ajaxlivesearch_guard&ls_token=${iid}&ls_page_loaded_at=0&ls_current_page=1&ls_query_id=ls_query&ls_query=${value}&ls_items_per_page=10000` }); const data = await post.text(); // Display results on page const resultPrint = data.replace(/\\/g,'').match(/<td style=''>.*?<\/td><td style=''><img src=".*?">/g).slice(1).toString().replace(/<\/td><td style=''><img /g,'" ').replace(/<td style=''>/g,'<img name="').replace(/,/g,'').replace(/src/g,`class="${slot}" src`); document.querySelector("#search").innerHTML = resultPrint; // Loop through results to add event listeners so when clicked the dude and list get updated const resultSelect = document.querySelectorAll(`.${slot}`) for (let i = 0; i < resultSelect.length; i++) { const node = resultSelect[i]; node.addEventListener('click', function() { const img = node.outerHTML.match(/src="([^"]*)"/i)[1] const name = node.outerHTML.match(/name="([^"]*)"/i)[1] document.querySelector(`#${slot}`).innerHTML = `<img src="${img}">` document.querySelector(`#txt${slot}`).innerHTML = name; }); }; }); }; }; // App custom theme async function appCustomTheme(){ // Close apps menu await appsMenuClose(); // Styling GM_addStyle(` .widget {-webkit-box-shadow: 0px 0px 3px 3px rgba(0,0,0,1);} .widget-content-area {-webkit-box-shadow: 0px 0px 3px 3px rgba(0,0,0,1);} #premadeThemes {white-space: nowrap;padding:15px;border-radius:10px;} .premadeDiv {display:inline-block;text-align:center;font-size:9px;font-family:monospace;} img.premade{transition: .5s ease-out;height:100px;width:100px;border-radius:10px;box-shadow:0 5px 5px 0 rgba(0,0,0,1);border:2px #475254 SOLID;margin:10px;cursor:pointer;} img.premade:hover{filter: saturate(250%);} `) // HTML document.querySelector("#content").innerHTML = ` <!-- Header Widget --> <div class="widget" style="width:1115px !important;margin-top:0.5rem;"> <h3>Outwar Theme</h3> Enter all colors as 6-digit <a href="https://htmlcolorcodes.com/" target="_blank">hexadecimal</a> codes <hr> <button class="btn-mm" id="updateTheme">UPDATE THEME</button><button class="btn-mm" id="resetTheme">RESET THEME</button> </div> <!-- Premade Themes --> <div class="widget" style="width:1115px !important;margin-top:1rem;"> <div class="list-group-item" style="width:100%;overflow:auto;text-align:left;max-width:100% !important;" id="premadeThemes"> </div> </div> <!-- Lower Widget --> <div class="row justify-content-center"> <!-- Left --> <div style="margin:1rem;width:350px"> <!-- Background Image --> <div class="widget widget-chart-one mb-3" style="text-align:left"> background image url <input style="width:100%;" id="themeBgImg" type="text" class="form-control-new mb-2 theme-input" autocomplete="off"> </div> <!-- Text and links --> <div class="widget widget-chart-one mb-3" style="text-align:left"> text color <input style="width:100%;" id="themeTxt" type="text" class="form-control-new mb-2 theme-input" autocomplete="off"> link color <input style="width:100%;" id="themeLink" type="text" class="form-control-new mb-2 theme-input" autocomplete="off"> link hover color <input style="width:100%;" id="themeLinkHover" type="text" class="form-control-new mb-2 theme-input" autocomplete="off"> </div> <!-- Scrollbar --> <div class="widget widget-chart-one mb-3" style="text-align:left"> scrollbar slider color <input style="width:100%;" id="themeScrollSlider" type="text" class="form-control-new mb-2 theme-input" autocomplete="off"> scrollbar track color <input style="width:100%;" id="themeScrollTrack" type="text" class="form-control-new mb-2 theme-input" autocomplete="off"> </div> </div> <!-- Middle --> <div style="margin:1rem;width:350px"> <!-- Toolbar and Widget Color --> <div class="widget widget-chart-one mb-3" style="text-align:left"> toolbar background color <input style="width:100%;" id="themeToolbarBg" type="text" class="form-control-new mb-2 theme-input" autocomplete="off"> main content background color <input style="width:100%;" id="themeContentBg" type="text" class="form-control-new mb-2 theme-input" autocomplete="off"> </div> <!-- Tables --> <div class="widget widget-chart-one mb-3" style="text-align:left"> table background color <input style="width:100%;" id="themeTableBg" type="text" class="form-control-new mb-2 theme-input" autocomplete="off"> table header text color <input style="width:100%;" id="themeTableHeaderTxt" type="text" class="form-control-new mb-2 theme-input" autocomplete="off"> table text color <input style="width:100%;" id="themeTableTxt" type="text" class="form-control-new mb-2 theme-input" autocomplete="off"> </div> </div> <!-- Right --> <div style="margin:1rem;width:350px"> <!-- Text Boxes and Buttons --> <div class="widget widget-chart-one mb-3" style="text-align:left"> button and textbox background color <input style="width:100%;" id="themeBoxBg" type="text" class="form-control-new mb-2 theme-input" autocomplete="off"> button and textbox text color <input style="width:100%;" id="themeBoxTxt" type="text" class="form-control-new mb-2 theme-input" autocomplete="off"> </div> <!-- Menu --> <div class="widget widget-chart-one mb-3" style="text-align:left"> menu background color <input style="width:100%;" id="themeMenuBg" type="text" class="form-control-new mb-2 theme-input" autocomplete="off"> menu text color <input style="width:100%;" id="themeMenuTxt" type="text" class="form-control-new mb-2 theme-input" autocomplete="off"> menu hover color <input style="width:100%;" id="themeMenuHover" type="text" class="form-control-new mb-2 theme-input" autocomplete="off"> menu subtext color <input style="width:100%;" id="themeMenuSubTxt" type="text" class="form-control-new mb-2 theme-input" autocomplete="off"> menu subtext hover <input style="width:100%;" id="themeMenuSubTxtHover" type="text" class="form-control-new mb-2 theme-input" autocomplete="off"> </div> </div> </div> ` // Specs for premade themes const premadeDetails = { 1: ['vordyn','F6DCF5','EE6CD7','F8C2F2','EE6CD7','F8C2F2','842266','300E25','292438','EE6CD7','F8C2F2','191320','F6DCF5','191320','F6DCF5','842266','EE6CD7','F8C2F2'], 2: ['alvar','CDE5FA','45B0DB','274E83','274E83','0D2F4C','0D2F4C','0B1426','08335E','274E83','CDE5FA','274E83','CDE5FA','274E83','CDE5FA','45B0DB','CDE5FA','45B0DB'], 3: ['delruk','FDE9D6','FDBB5F','FE7626','FE7626','6A241C','6A241C','290C08','18080A','FE7626','FDE9D6','B84313','FDE9D6','18080A','FDBB5F','FE7626','B84313','FE7626'], 4: ['lagoon','B8C6C3','EDEEDB','23466A','23466A','161C2A','142335','0F151C','66706A','9BB1B5','B8C6C3','132A45','ABBDBE','141D2A','ABBDBE','727C76','9BB1B5','B8C6C3'], 5: ['triangle','FCFFFF','DA8D95','77573A','77573A','0F0613','0F0613','171C28','1D1221','DA8D95','FCFFFF','77573A','FCFFFF','142936','FCFFFF','77573A','DA8D95','77573A'], 6: ['cyberpunk','C1C5CD','FD3FF5','6EC4EF','6EC4EF','1A1A16','1C1C3C','1A1A16','281842','6EC4EF','C1C5CD','1C1C3C','C1C5CD','04202D','FD3FF5','6EC4EF','C1C5CD','6EC4EF'], 7: ['haze','FDFDFD','AE9ADE','EFB9EB','EFB9EB','181F3A','303148','181F3A','39374E','EFB9EB','FDFDFD','3A3E51','EFB9EB','3A3E51','FDFDFD','AE9ADE','AE9ADE','FDFDFD'], 8: ['leather','ffffff','907552','ffffff','907552','272017','272017','070508','272017','907552','ffffff','272017','ffffff','11120F','ffffff','272017','ffffff','907552'], 9: ['doodle','1F94BA','15C391','85446C','1F94BA','1D1C21','1D1C21','1D1C21','1D1C21','576590','873F63','1E9EC2','1D1C21','1D1C21','645A7D','18A9AA','18A9AA','15C391'], 10: ['ring','ffffff','E7A300','ffffff','E7A300','1D1D1D','070707','0C0C0C','1D1D1D','E7A300','ffffff','1D1D1D','ffffff','1D1D1D','E7A300','0C0C0C','ffffff','E7A300'], 11: ['woods','d5d5d5','828282','d5d5d5','3D3D3D','111111','1A1A1A','111111','242424','828282','d5d5d5','0D0D0D','d5d5d5','1A1A1A','d5d5d5','5B5B5B','5B5B5B','d5d5d5'], 12: ['view','CEE4EA','5AC5D1','5A8081','5AC5D1','12353D','12353D','030911','0E312C','5A8081','CEE4EA','153833','0E312C','CEE4EA','B3D6E3','5A8081','5AC5D1'], 13: ['carbon','BFBDBC','C61C30','43413F','E22F35','050303','050303','1F1C1D','1F1C1D','C61C30','BFBDBC','1F1C1D','E22F35','0E0C0D','BFBDBC','43413F','BFBDBC','C61C30'], 14: ['classic','d3d3d3','d3d3d3','fff000','3D3D3D','1B1B1B','1B1B1B','0b0b0b','232323','fff000','d3d3d3','282828','d3d3d3','0b0b0b','d3d3d3','232323','fff000','d3d3d3'], 15: ['sunrise','FAC7DC','50B3B1','DB5B7F','50B3B1','122034','0F1E32','09181F','122034','DB5B7F','FAC7DC','221424','C95175','3F2E42','50B3B1','FF90A1','47AAAF','FAC7DC'], 16: ['spotlight','b2b2b2','939393','FFFFFF','b2b2b2','181818','060606','181818','262626','b2b2b2','939393','262626','b2b2b2','181818','b2b2b2','262626','b2b2b2','939393'], 17: ['calibrate','ffffff','CEF382','EE9B5A','EE9B5A','024671','5c3047','234259','024671','CEF382','d1a8a2','3c274c','EE7462','003150','CEF382','4D69A1','EE9B5A','d1a8a2'], 18: ['lake','7AB0EE','908D5C','5896E0','76ADE1','0F2336','2E3447','0B0E16','2E3543','76ADE1','ffffff','2E3543','ffffff','0B0E16','ffffff','7AB0EE','D3A145','76ADE1'], 19: ['osiris','67C5F8','F7F554','ffffff','67C5F8','111219','13161E','191925','333340','ffffff','ffffff','090A11','ffffff','090A11','ffffff','0754A1','67C5F8','F7F554'], 20: ['cove','ffffff','14E7D9','EE9B5A','EE9B5A','171954','5c3047','120C42','024671','CEF382','d1a8a2','3c274c','EE7462','210D51','EA9DAC','4D69A1','EE9B5A','d1a8a2'], 21: ['river','D9E4F6','C3E5F2','E57EAC','D9E4F6','121E2A','080C12','080C12','061A41','D9E4F6','E57EAC','121E2A','E57EAC','121E2A','E57EAC','061A41','D9E4F6','C3E5F2'] }; // Add premade themes to theme selecting page const premadeCount = Object.keys(premadeDetails).length; const premadeArray = []; for (let i = 1; i <= premadeCount; i++) { premadeArray.push(` <div class="premadeDiv"> <img src="https://studiomoxxi.com/moxximod/plus_wallpapers/${i}.webp" class="premade" id="premade${i}"><br> ${premadeDetails[i][0].toUpperCase()} </div> `); }; // Premade theme button clicks document.querySelector("#premadeThemes").innerHTML = premadeArray.join(''); for (let i = 1; i <= premadeCount; i++) { document.querySelector(`#premade${i}`).addEventListener('click', async function(){ // Add wallpaper URL document.querySelector("#themeBgImg").value = `https://studiomoxxi.com/moxximod/plus_wallpapers/${i}.webp` // Get all other inputs const inputs = document.querySelectorAll('input.theme-input'); // Add all other input values if they exist in the array for (let x = 1; x < inputs.length; x++) { if (premadeDetails[i][x]){ inputs[x].value = premadeDetails[i][x]; }; }; }); }; // Update theme function document.querySelector("#updateTheme").addEventListener('click', async function(){ // Select all textboxes const inputs = document.querySelectorAll('input.theme-input'); // Iterate through all textboxes const hexArray = []; inputs.forEach(async element => { // Get the value and id for each textbox const value = element.value.replace(/#/g,''); const id = element.id // If the value is blank or a 6-digit value process, if not error except for background image if ((/^\w{6}$/.test(value) || id == "themeBgImg") || value == '') { if (value != ''){ GM_setValue(id,value); if (!value.match('studiomoxxi')){ hexArray.push(`'${value}'`); }; }; } else { alert(`Invalid entry for ${id}. Please make sure all values are 6-digit hex codes`); return; }; }); // Retain this console log for developing themes console.log(`Theme value array: ${hexArray.join(',')}`) await loadCustomTheme(); }); // Reset theme function document.querySelector("#resetTheme").addEventListener('click', async function(){ const inputs = document.querySelectorAll('input.theme-input'); inputs.forEach(async element => { const id = element.id GM_deleteValue(id); }); window.location = window.location; }); }; // Apply custom theme async function loadCustomTheme(){ if (GM_getValue("themeBoxBg")){ const value = GM_getValue("themeBoxBg"); GM_addStyle(` .form-control-new {background:#${value} !important} .form-control {background:#${value} !important} .btn-mm {background-color:#${value} !important;} .btn-primary {background-color:#${value} !important;border:0px SOLID !important;} .btn {background-color:#${value} !important;border:0px SOLID !important;} .swal2-popup {background-color:#${value} !important} .btn-info{background-color:#${value} !important;} .alert-light-warning{background-color:#${value} !important;} `); }; if (GM_getValue("themeBoxTxt")){ const value = GM_getValue("themeBoxTxt"); GM_addStyle(` .form-control-new {color:#${value} !important} .form-control-new::placeholder {color:#${value} !important;opacity:0.3;} .form-control {color:#${value} !important} .form-control::placeholder {color:#${value} !important;opacity:0.3;} .btn-mm {color:#${value} !important;} .btn-primary {color:#${value} !important;border:0px SOLID !important;} .btn {color:#${value} !important;border:0px SOLID !important;} .swal2-popup {color:#${value} !important} .btn-info {color:#${value} !important} .alert-light-warning{color:#${value} !important} #mailCollapseTwo > div > div.alert.alert-light-warning.border-0.mb-4 > button > svg{color:#${value} !important} body{color:#${value} !important;} `); }; if (GM_getValue("themeBgImg")){ const value = GM_getValue("themeBgImg"); var height = window.screen.availHeight var width = window.screen.availWidth GM_addStyle(` body > center{background-image: url("${value}") !important; background-size: ${width}px ${height}px !important; background-attachment: fixed !important; background-position:center !important; background-repeat:no-repeat !important; `); }; if (GM_getValue("themeScrollSlider") && GM_getValue("themeScrollTrack")){ const value1 = GM_getValue("themeScrollSlider"); const value2 = GM_getValue("themeScrollTrack"); GM_addStyle(` * {scrollbar-color: #`+value1+` #`+value2+`;} *::-webkit-scrollbar-track {background:#`+value2+`;} *::-webkit-scrollbar-thumb {background-color:#`+value1+`;} * {scrollbar-width: 7px;} *::-webkit-scrollbar {width: 7px;} `); }; if (GM_getValue("themeToolbarBg")){ const value = GM_getValue("themeToolbarBg"); GM_addStyle(` .navbar {background:#${value} !important;} #sidebar ul.menu-categories li.menu > .dropdown-toggle[aria-expanded=true] {background:#${value} !important;} .nav-link.active {background-color: #${value} !important; border-color: #${value} !important;} .nav-tabs {border-bottom: #${value} !important;} `); }; if (GM_getValue("themeContentBg")){ const value = GM_getValue("themeContentBg"); GM_addStyle(` .widget {background-color:#${value}} .widget-content-area {background-color:#${value}} .widget-content {background-color:#${value}} .custom-dropdown-menu {background-color:#${value} !important;} .swal2-modal {background-color: #${value} !important;} #bpWin_handle {background-color: #${value} !important;} #eqWin_handle {background-color: #${value} !important;} .widget-header{background-color:#${value} !important;} .modal-content{background-color:#${value} !important;} .mailbox-inbox{background-color:#${value} !important;} .content-box{background-color:#${value} !important;} .msg-close{background:#${value} !important;} `); }; if (GM_getValue("themeTableBg")){ const value = GM_getValue("themeTableBg"); GM_addStyle(` .table {background-color:#${value} !important;} .table-striped {background-color:#${value} !important;} .list-group-item {background-color: #${value} !important;} .wquesttable{background-color: #${value} !important;} .list-group-item {border: #${value} 1px SOLID !important;margin-bottom:4px !important;} .b-skills {background-color: #${value} !important;} hr {border-top: 1px SOLID #${value} !important;} .skillsbox{background-color:#${value} !important;} .modal-footer{border-top: 1px SOLID #${value} !important;} .modal-header{border: 1px SOLID #${value} !important;} .page-link{background:#${value} !important;} .page-link{background:#${value} !important;} #zero-config_info {border: 1px SOLID #${value} !important;} #mailHeadingEleven > div > div{background:#${value} !important;} #content-header-row > div > div > div > div > div.tab-title{background:#${value} !important;} p.mail-content.text-left{border-top: 1px SOLID #${value} !important;} div.d-flex.msg-close{border-bottom: 1px SOLID #${value} !important;} td[id*="questStep"]{background-color:#${value} !important;} `); }; if (GM_getValue("themeTableHeaderTxt")){ const value = GM_getValue("themeTableHeaderTxt"); GM_addStyle(` .table > thead > tr > th {color:#${value} !important;} .table {border:#${value} SOLID 1px !important;} .table > tbody > tr {border:#${value} SOLID 1px !important;} .table > thead {border-top:#${value} SOLID 1px !important;} .table > border-collapse: revert !important;} .table-striped > thead > tr > th {color:#${value} !important;} .table-striped {border:#${value} SOLID 1px !important;} .table-striped > tbody > tr {border:#${value} SOLID 1px !important;} .table-striped > thead {border-top:#${value} SOLID 1px !important;} .table-striped > border-collapse: revert !important;} table.dataTable > thead > tr > th {color:#${value} !important;} table.dataTable {border:#${value} SOLID 1px !important;} table.dataTable > tbody > tr {border:#${value} SOLID 1px !important;} table.dataTable > thead {border-top:#${value} SOLID 1px !important;} table.dataTable {border-collapse: revert !important;} `); }; if (GM_getValue("themeTableTxt")){ const value = GM_getValue("themeTableTxt"); GM_addStyle(` .table > tbody > tr > td {color:#${value} !important;} .table-striped > tbody > tr > td {color:#${value} !important;} #eqWin_handle > tbody > tr > td {color:#${value} !important;} #bpWin_handle > tbody > tr > td {color:#${value} !important;} `); }; if (GM_getValue("themeTxt")){ const value = GM_getValue("themeTxt"); GM_addStyle(` .list-group.list-group-media{color:#${value} !important;} .list-group.list-group-media{color:#${value} !important;} .list-group-item{color:#${value} !important;} .media{color:#${value} !important;} .media-body{color:#${value} !important;} h6{color:#${value} !important;} h5{color:#${value} !important;} h4{color:#${value} !important;} h3{color:#${value} !important;} .list-group-item{color:#${value} !important;} .media{color:#${value} !important;} .media-body{color:#${value} !important;} p{color:#${value} !important;} .list-group-item{color:#${value} !important;} .bio{color:#${value} !important;} .widget{color:#${value} !important;} .widget-content-area{color:#${value} !important;} #cvMsg{color: #${value} !important;} div.dataTables_wrapper{color: #${value} !important;} div.dataTables_length label{color: #${value} !important;} `); }; if (GM_getValue("themeLink")){ const value = GM_getValue("themeLink"); GM_addStyle(` a:not(.dropdown-toggle):not(.form-control-new){color:#${value} !important;} .form-control-new{border: 1px SOLID #${value} !important;} .form-control{border: 1px SOLID #${value} !important;} div.dataTables_wrapper{color:#${value} !important;} div.dataTables_info{color:#${value} !important;} #mailInbox > svg{color:#${value} !important;} #draft > svg{color:#${value} !important;} #sentmail > svg{color:#${value} !important;} #trashed > svg{color:#${value} !important;} #btn-compose-mail{display:none !important;} td[id*="questStep"] > a > font > b{color:#${value} !important;} `); }; if (GM_getValue("themeLinkHover")){ const value = GM_getValue("themeLinkHover"); GM_addStyle(` a:not(.dropdown-toggle):not(.form-control-new):hover{color:#${value} !important;} `); }; if (GM_getValue("themeMenuBg")){ const value = GM_getValue("themeMenuBg"); GM_addStyle(` #accordionExample{background:#${value} !important;} #sidebar{background:#${value} !important;} .dropdown-menu {background: #${value} !important;border:0px SOLID !important;} .nav-link.active{background-color:#${value} !important;} .nav-link.active{border-color:#${value} !important;} `); }; if (GM_getValue("themeMenuTxt")){ const value = GM_getValue("themeMenuTxt"); GM_addStyle(` a.dropdown-toggle {color:#${value} !important;} #sidebar ul.menu-categories li.menu>.dropdown-toggle svg {color:#${value} !important;} `); }; if (GM_getValue("themeMenuHover")){ const value = GM_getValue("themeMenuHover"); GM_addStyle(` #sidebar ul.menu-categories li.menu > .dropdown-toggle.dropdown-toggle:hover {background:#${value} !important;} .btn-group .dropdown-menu a.dropdown-item:hover {background:#${value} !important;} .btn-group .dropdown-menu .dropdown-item:hover {background:#${value} !important;} `); }; if (GM_getValue("themeMenuSubTxt")){ const value = GM_getValue("themeMenuSubTxt"); GM_addStyle(` #sidebar ul.menu-categories ul.submenu > li a {color:#${value} !important;} `); }; if (GM_getValue("themeMenuSubTxtHover")){ const value = GM_getValue("themeMenuSubTxtHover"); GM_addStyle(` #sidebar ul.menu-categories ul.submenu>li a:hover:before {background-color:#${value} !important;} #sidebar ul.menu-categories ul.submenu > li a:hover {color:#${value} !important;} `); }; }; // App chaos teleporters async function appChaosTeles(server){ document.querySelector("#overlayWidget").innerHTML = ` <center> <img src="https://studiomoxxi.com/moxximod/loading-gif.gif" style="height:100px;width:100px">` const charDropdown = document.body.innerHTML.replace(/[\n\r]/g,'').match(/<optgroup label="My Characters">.*?<option value="0">--Change Server--<\/option>/i) const charIdArray = charDropdown.toString().match(/value="[0-9]+"/g).map(match => match.match(/[0-9]+/)[0]).slice(0, -1); let tableRows = [] let totalTeles = 0 const inventory = async (id) => { const kBp = await superfetch(`ajax/backpackcontents.php?tab=key&suid=${id}`); if (kBp.match(/data-itemidqty="[0-9]+" data-name="Arena Teleporter"/i)){ const teleporters = parseInt(kBp.match(/data-itemidqty="([0-9]+)" data-name="Arena Teleporter"/i)[1]); const profileData = await superfetchProfile(`profile?suid=${id}`); const name = profileData.name; const rage = profileData.currentrage.toLocaleString(); const circ = profileData.skills.list.match('Circumspect') ? "Yes" : "No" const power = profileData.power.toLocaleString(); const ele = profileData.elemental.toLocaleString(); tableRows.push(`<tr><td>${name}</td><td>${power}</td><td>${ele}</td><td>${rage}</td><td>${circ}</td><td>${teleporters}</td></tr>`); totalTeles += teleporters; }; }; await Promise.all(charIdArray.map(inventory)); document.querySelector("#overlayWidget").innerHTML = ` <table class="table table-striped mb-3" id="arenaTeleTable"> <thead><tr><th>Character</th><th>Power</th><th>Elemental</th><th>Rage</th><th>Circumspect Cast</th><th>Arena Teleporters</th></thead> <tbody> ${tableRows.join('')} </tbody> </table> <brr><center> <button class="btn-mm" id="useTeleporters"></button> ` if (totalTeles > 0){ document.querySelector("#useTeleporters").innerHTML = `USE ${totalTeles} ARENA TELEPORTERS` document.querySelector("#useTeleporters").addEventListener('click', async function() { document.querySelector("#useTeleporters").remove(); await mmplus(`ChaosTele|rganame|${server}`); }); } else { document.querySelector("#arenaTeleTable").remove(); document.querySelector("#useTeleporters").outerHTML = `YOU HAVE NO ARENA TELEPORTERS TO USE` }; }; // App backpack async function appBackpack(server,serverNo,rgaName,charId){ // Styling GM_addStyle(` #backpackDiv{height:90%;background-color:#000000;box-shadow: 5px 5px 5px rgba(0, 0, 0, 1);padding:15px} #actionButtons{text-align:center;margin-top:1rem;} div.bp-widget{display:inline-block;border-radius:10px;} div.bp-items-div{height:140px;overflow:auto;padding:10px;border-radius:0px;width:300px;text-align:left;} div.bp-slot{background-image: url("/images/bp/bp_tile.gif");width: 60px;height: 60px;margin: 1px;position: relative;display:inline-block;} img.item-selectable{height:46px;width:46px;margin:7px;border-radius:7px;transition: 0.1s ease-out;cursor:pointer;} img.item-selected{border:5px #00CC00 SOLID;padding:3px;height:60px;width:60px;margin:0px;} `) // HTML document.querySelector("#overlayWidget").innerHTML = ` <div id="backpackDiv" style="text-align:center;overflow:auto;"> <img src="https://studiomoxxi.com/moxximod/loading-gif.gif" style="height:100px;width:100px;margin-top:2rem;"> </div> <div id="actionButtons"></div> ` // Build widgets const ajax = await superfetch(`ajax/accounts.php?t_serv=${serverNo}`); const charIds = ajax.match(/"id":"[0-9]+"/g).map(i => i.match(/[0-9]+/)); const widgets = []; const fetchProfilesAndBackpacks = async (id) => { const profile = await superfetch(`profile?suid=${id}`,true); const name = profile.match(/<font size="3">(.*?)<\/font>/i)[1]; const parser = new DOMParser(); const profileHtml = parser.parseFromString(profile, 'text/html'); const bp = await superfetch(`ajax/backpackcontents?tab=regular&suid=${id}`,true); const items = bp.match(/data-iid="[0-9]+" id="[^"]*" class="[^"]*" src="[^"]*"/g) || []; const allItems = []; items.forEach(item => { const img = item.match(/src="([^"]*)"/)[1]; const iid = item.match(/data-iid="([0-9]+)"/)[1]; allItems.push(` <div class="bp-slot"> <img src="${img}" class="item-selectable" id="${id}" onmouseover="itempopup(event,'${iid}')" onmouseout="kill()"> </div> `); }); const thedude = profileHtml.querySelector('div[style="position:relative; width:300px; height:385px; background-image:url(/images/thedude.png)"]').outerHTML.replace(/onclick="[^"]*"/g,''); widgets.push(` <div class="bp-widget"> <div class="list-group-item"><h5>${name}</h5></div> ${thedude} <div class="bp-items-div list-group-item">${allItems.join('')}</div> </div> `) }; await Promise.all(charIds.map(fetchProfilesAndBackpacks)); document.querySelector("#backpackDiv").innerHTML = widgets.join(''); // Item onclick features const itemSelectable = document.querySelectorAll(".item-selectable"); itemSelectable.forEach(item => { item.addEventListener('click', async function(){ item.classList.toggle('item-selected'); }); }); // Action buttons document.querySelector("#actionButtons").innerHTML = ` <button class="btn-mm" id="bpActivate">ACTIVATE</button> <button class="btn-mm" id="bpEquip">EQUIP</button> <button class="btn-mm" id="bpVault">VAULT</button> ` document.querySelector("#bpActivate").addEventListener('click', async function(){ await bpAction('Activate') }); document.querySelector("#bpEquip").addEventListener('click', async function(){ await bpAction('Equip') }); document.querySelector("#bpVault").addEventListener('click', async function(){ await bpAction('Vault') }); // Action function async function bpAction(type){ // Get data from selected items const actionString = []; const selectedItems = document.querySelectorAll(".item-selected"); const parseSelectedItems = async (item) => { const charid = item.outerHTML.match(/id="([0-9]+)"/i)[1]; const itemid = item.outerHTML.match(/event,'([0-9]+)'/i)[1]; actionString.push(`rganame&${charid}&${itemid}`); }; await Promise.all(Array.from(selectedItems).map(parseSelectedItems)); // Rebuild loading screen document.querySelector("#overlayWidget").innerHTML = ` <div id="backpackDiv" style="text-align:center;overflow:auto;"> <img src="https://studiomoxxi.com/moxximod/loading-gif.gif" style="height:100px;width:100px;margin-top:2rem;"> </div> <div id="actionButtons"></div> ` await mmplus(`${type}|rganame|${server}|${actionString.join(',')}`); await appBackpack(server,serverNo,rgaName,charId); }; }; // App wilderness async function appWilderness(server,serverNo,rgaName,charId){ // Styling GM_addStyle(` #overlayWidget{text-align:center;} `); async function wilderness() { const fetch = await superfetch('wilderness',true); const parser = new DOMParser(); const doc = parser.parseFromString(fetch, 'text/html'); const wildernessContent = doc.querySelector("#divWildernessContainer > div.row.mx-3.mt-3 > div:nth-child(2)").innerHTML.replace(`<a id="wildernessLink"`,`<a id="wildernessLink" TARGET="wildatk"`); const level = doc.querySelector("#divHeader > h1").innerHTML; document.querySelector("#overlayWidget").innerHTML = ` <h6>Level ${level}</h6><p> ${wildernessContent} <p>Click above button once to attack mob<br>Mob will update once defeated<p style="margin-top:1.5rem"> <p style="margin-top:1rem;"> <button id="spamWilderness" class="btn-mm">SPAM ATTACK</button><p> <span id="spamCnt"></span><p> <iframe id="wildatk" name="wildatk" src="" style="opacity: 0 !important;height:0px !important;width:0px !important;"></iframe> `; document.querySelector("#spamWilderness").addEventListener("click", spam); document.querySelector("#wildernessLink > img").addEventListener("click", frame); }; function frame() { wilderness(); document.querySelector("#wildernessLink > img").click(); }; // Spam wilderness feature let count = 0; function spam() { setTimeout(function() { document.querySelector("#wildernessLink > img").click(); count += 1; document.querySelector("#spamCnt").innerHTML = count; spam(); }, 250); }; // Start by fetching wilderness content wilderness(); } async function appWilderness_old(server,serverNo,rgaName,charId){ // Styling GM_addStyle(` div.wilderness-div{width:100%;text-align:center;} img.wilderness-mob-img{border-radius:10px;box-shadow:0 5px 5px 0 rgba(0,0,0,1);margin-bottom:1rem;height:250px;width:250px;} input.txt {background: #FFFFFF !important;color: #000000 !important;opacity: 1 !important;} .wilderness-info{margin-bottom:1rem;width:250px;} .wilderness-info-div{display:inline-block;margin-right:4px;margin-left:4px;} `) // HTML document.querySelector("#overlayWidget").innerHTML = ` <div class="wilderness-div"> <div id="wildernessLvl"></div> <div id="wildernessImg"><img src="https://studiomoxxi.com/moxximod/loading-gif.gif" style="height:100px;width:100px;margin-bottom:1rem;"></div> <div id="wildernessInfo"> <div class="wilderness-info-div"> <div class="form-control-new wilderness-info" style="padding:1rem;opacity:1 !important;"> WINS<br> <input id="wildernessWins" type="number" class="form-control-new txt" style="width:180px;" disabled> </div><br> <div class="form-control-new wilderness-info" style="padding:1rem;opacity:1 !important;"> LOSSES<br> <input id="wildernessLosses" type="number" class="form-control-new txt" style="width:180px;" disabled> </div> </div> <div class="wilderness-info-div"> <div class="form-control-new wilderness-info" style="padding:1rem;opacity:1 !important;"> TOTAL<br> <input id="wildernessTotal" type="number" class="form-control-new txt" style="width:180px;" disabled> </div><br> <div class="form-control-new wilderness-info" style="padding:1rem;opacity:1 !important;"> BEST <img src="images/questwiki.jpg" onmouseover="statspopup(event,'% health remaining of best attack against current mob')" onmouseout="kill()" style="margin-bottom:5px;"><br> <input id="wildernessBest" type="number" class="form-control-new txt" style="width:180px;" disabled> </div> </div> </div> <button class="btn-mm" id="startWilderness">START</button> </div> ` const atkLink = await appWildernessLoad() document.querySelector("#startWilderness").addEventListener('click', async function(){ await appWildernessAttack(atkLink,0,0,100000000); }); }; async function appWildernessLoad(){ const wilderness = await superfetch('wilderness',true); const atkLink = wilderness.match(/id="wildernessLink" href="([^"]*)"/i)[1]; document.querySelector("#wildernessLvl").innerHTML = `<h1> LEVEL ${wilderness.match(/<h1>(.*?)<\/h1>/i)[1]}</h1>`; document.querySelector("#wildernessImg").innerHTML = `<img src="${wilderness.match(/src="([^"]*)" width="250" height="250"/i)[1]}" class="wilderness-mob-img">`; return atkLink; }; async function appWildernessAttack(atkLink,wins,losses,best){ // Attack const attack = await superfetch(atkLink,true); // If win if (attack.match(/var successful = 1/i)){ wins += 1; best = 100000000; atkLink = await appWildernessLoad(); // If lose } else { losses += 1; const mobHp = parseInt(attack.match(/var defender_health_start = ([0-9]+)/i)[1]); const mobBaseDmgTaken = (attack.match(/defender_taken\[[0-9]+\] = '[0-9]+'/g) || []).reduce((sum, match) => sum + parseInt(match.match(/'([0-9]+)'/)[1]), 0); const mobHolyDmgTaken = (attack.match(/defender_elemental_taken\[[0-9]+\]\[[0-9]+\] = new Damage\([0-9]+, 'holy'\)/g) || []).reduce((sum, match) => sum + parseInt(match.match(/\(([0-9]+)/)[1]), 0); const mobArcnDmgTaken = (attack.match(/defender_elemental_taken\[[0-9]+\]\[[0-9]+\] = new Damage\([0-9]+, 'arcane'\)/g) || []).reduce((sum, match) => sum + parseInt(match.match(/\(([0-9]+)/)[1]), 0); const mobShadDmgTaken = (attack.match(/defender_elemental_taken\[[0-9]+\]\[[0-9]+\] = new Damage\([0-9]+, 'shadow'\)/g) || []).reduce((sum, match) => sum + parseInt(match.match(/\(([0-9]+)/)[1]), 0); const mobFireDmgTaken = (attack.match(/defender_elemental_taken\[[0-9]+\]\[[0-9]+\] = new Damage\([0-9]+, 'fire'\)/g) || []).reduce((sum, match) => sum + parseInt(match.match(/\(([0-9]+)/)[1]), 0); const mobKinkDmgTaken = (attack.match(/defender_elemental_taken\[[0-9]+\]\[[0-9]+\] = new Damage\([0-9]+, 'kinetic'\)/g) || []).reduce((sum, match) => sum + parseInt(match.match(/\(([0-9]+)/)[1]), 0); const mobChosDmgTaken = (attack.match(/defender_elemental_taken\[[0-9]+\]\[[0-9]+\] = new Damage\([0-9]+, 'chaos'\)/g) || []).reduce((sum, match) => sum + parseInt(match.match(/\(([0-9]+)/)[1]), 0); const mobRemainingHp = Math.max((mobHp-mobBaseDmgTaken-mobHolyDmgTaken-mobArcnDmgTaken-mobShadDmgTaken-mobFireDmgTaken-mobKinkDmgTaken-mobChosDmgTaken),0); const percent = ((mobRemainingHp/mobHp).toFixed(5))*100; if (percent < best){ best = percent; }; }; document.querySelector("#wildernessWins").value = wins document.querySelector("#wildernessLosses").value = losses document.querySelector("#wildernessTotal").value = wins+losses document.querySelector("#wildernessBest").value = best await appWildernessAttack(atkLink,wins,losses,best); }; // App saved rgas async function appSavedRgas(server,serverNo,rgaName,charId){ // HTML element document.querySelector("#overlayWidget").innerHTML = ` <div style="text-align:center;" id="saveRgasContainer"> <h6>List session ids seperated by commas or paste from OWH</h6><hr> <textarea id="sessionsTextArea" class="form-control-new mb-3" style="resize:none;" cols="70" rows="10"></textarea><br> <button class="btn-mm" id="saveRgas">SAVE RGAS</button> <button class="btn-mm" id="deleteRgas">RESET RGAS</button><hr> <span id="hud"></span> </div> ` // Load saved RGAs if (GM_getValue('savedRgas').match("option")){ const storage = GM_getValue('savedRgas') const sessions = storage.match(/rg_sess_id=[A-Za-z0-9]+/g).map(i => i.match(/rg_sess_id=([A-Za-z0-9]+)/)[1]); document.querySelector("#sessionsTextArea").value = sessions.join(',') }; // Error alert document.querySelector("#saveRgas").addEventListener('click',saveError); // Text box monitor const box = document.querySelector("#sessionsTextArea"); box.addEventListener('keyup', async function(){ box.value = box.value.replace(/.*?:/g,'').replace(/ /g,'').replace(/[\n\r]/g,','); const count = (box.value.match(/\b[a-zA-Z0-9]{32}\b/g) || []).length; // Valid entry if (count >= 1){ document.querySelector("#saveRgas").addEventListener('click',saveRgas); document.querySelector("#saveRgas").removeEventListener('click',saveError); document.querySelector("#hud").innerHTML = `Found ${count} sessions` }; // Invalid entry const characters = box.value.length; if (count != (characters+1)/33){ document.querySelector("#saveRgas").removeEventListener('click',saveRgas); document.querySelector("#saveRgas").addEventListener('click',saveError); document.querySelector("#hud").innerHTML = `ERROR: Invalid entry` }; // Reset if (box.value == ''){ document.querySelector("#saveRgas").removeEventListener('click',saveRgas); document.querySelector("#saveRgas").addEventListener('click',saveError); document.querySelector("#hud").innerHTML = '' }; }); // Save RGAs function async function saveRgas(){ document.querySelector("#saveRgasContainer").innerHTML = '<img src="https://studiomoxxi.com/moxximod/loading-gif.gif" height="80px" width="80px">' const postedRgas = await mmplus(`CustomRGA|rganame|${server}|${box.value}`); GM_setValue('savedRgas', postedRgas.replace(/value="home/g,`value="https://${server}.outwar.com/home`)); await blankOff(); }; // Error alert function async function saveError(){ alert('ERROR: Invalid entry'); }; // Delete saved RGAs function document.querySelector("#deleteRgas").addEventListener('click', function(){ GM_deleteValue('savedRgas'); alert('Saved RGAs have been cleared'); document.querySelector("#sessionsTextArea").value = ''; }); }; // App raids report async function appRaidsReport(server,serverNo,rgaName,charId){ GM_addStyle(` img.raidsReportImg{box-shadow: 5px 5px 5px rgba(0, 0, 0, 1);cursor:pointer;transition: .5s ease-out;width:110px;height:110px;border-radius:5px;position:inline-block;margin-bottom:7px;animation: fadeIn 1s ease-in-out forwards;} @keyframes fadeIn {from {opacity: 0;} to {opacity: 1;}} img.raidsReportImg:hover{filter: saturate(250%);opacity:0.5;} div.reportImgDiv{display: inline-block;font-size: 9px;text-align: center;font-family: monospace,monospace;height:130px;width:130px;margin:10px;} `) document.querySelector("#overlayWidget").innerHTML = ` <div id="raidsReportDiv" style="height:100%;overflow:auto;text-align:center;"> <img src="https://studiomoxxi.com/moxximod/loading-gif.gif" style="height:100px;width:100px"> </div> ` const crewId = document.body.innerHTML.match(/crewid=([0-9]+)/i)[1] const raidResults = await superfetch(`crew_raidresults.php?all_results=Display+all+raid+results&crewid=${crewId}`); const targetsArray = raidResults.replace(/[\n\r]/g,'').replace(/[:\-]/g, "").match(/[0-9]+ [0-9]+[A-Za-z]+<\/td>.*?<td align="left" valign="top">.*?<\/td>/g).map(i => i.match(/<td align="left" valign="top">(.*?)<\/td>/i)[1]) let uniqueTargets = [...new Set(targetsArray)]; let imgArray = []; for (let i = 0; i < uniqueTargets.length; i++) { const godName = uniqueTargets[i] const nameTrimmed = godName.replace(/(,.*|of.*|The|the.*)/g, ''); const regex = new RegExp(`${godName.replace(/\s+/g,'')}<\\/td><tdalign="left"valign="top"><fontcolor="[^"]*"><b>[A-Za-z]+<\\/b><\\/font><\\/td><tdalign="left"valign="top"><ahref="raidattack\\.php\\?raidid=[0-9]+">`) const raidId = raidResults.replace(/[\n\r]/g,'').replace(/[:\-]/g, "").replace(/\s+/g,'').match(regex).map(i => i.match(/raidid=([0-9]+)/i)[1]); const raid = await superfetch(`raidattack.php?raidid=${raidId}`); const raidImg = raid.replace(/[\n\r]/g,'').replace(/\s+/g,'').match(/<divclass="defenderimageskinborderd-flexjustify-content-centeralign-items-centermb-3"><imgsrc="([^"]*)"><\/div>/i)[1] imgArray.push(`<div class="reportImgDiv"><img src="${raidImg}" id="${nameTrimmed.replace(/ /g,'').toLowerCase()}" alt="${godName}" class="raidsReportImg"><br>${nameTrimmed.toUpperCase()}</div>`); }; document.querySelector("#raidsReportDiv").innerHTML = ` <h6>Which raid target would you like to run a damage report for?</h6><hr> ${imgArray.join('')} ` // Create buttons const btnArray = document.querySelectorAll(".raidsReportImg"); for (let i = 0; i < btnArray.length; i++) { const btn = btnArray[i] btn.addEventListener('click',async function(){ const alt = btn.outerHTML.match(/alt="([^"]*)"/i)[1] const regex = new RegExp(`<td align="left" valign="top">${alt}<\/td>.*?raidid=[0-9]+">`,'g') const raidsArray = raidResults.replace(/[\n\r]/g,'').replace(/[:\-]/g, "").match(regex).map(i => i.match(/raidid=([0-9]+)/i)[1]); mmplus(`RaidsReport|rganame|${server}|${raidsArray.toString()}`); await blankOff(); }); }; }; // App item analyzer async function appItemAnalyzer(server,serverNo,rgaName,charId){ // Build HTML element document.querySelector("#overlayWidget").innerHTML = ` <div id="itemStorageDiv" style="text-align:center;height:100%;overflow:auto;"> <img src="https://studiomoxxi.com/moxximod/loading-gif.gif" style="height:100px;width:100px"> </div> ` // Get char IDs const charDropdown = document.body.innerHTML.replace(/[\n\r]/g,'').match(/<optgroup label="My Characters">.*?<option value="0">--Change Server--<\/option>/i) const charIdArray = charDropdown.toString().match(/value="[0-9]+"/g).map(match => match.match(/[0-9]+/)[0]).slice(0, -1); // Loop through equipment let tableData = []; const inventory = async (id) => { // Get all item IDs from equipment const fetchEq = await superfetch(`equipment?suid=${id}`); const itemsId = fetchEq.replace(/[\n\r]/g,'').replace(/<div style="position:absolute; left:10px; top:346px; width:32px; height:32px;text-align:center">.*/i,'').match(/event,'([0-9]+)'/g); // Loop through item IDs if (itemsId){ for (let i = 0; i < itemsId.length; i++) { const iid = itemsId[i].match(/[0-9]+/i)[0]; const item = await superfetch(`item_rollover.php?id=${iid}`); const name = (item.match(/align="left">(.*?)<\/td>/i) || ['',''])[1] const gems = 4-(item.match(/\/images\/gemslot2\.jpg/) ? item.match(/\/images\/gemslot2\.jpg/g).length : 0); const augs = item.match(/img width="9" style="border:1px solid #666666;margin:2px;"/) ? item.match(/img width="9" style="border:1px solid #666666;margin:2px;"/g).length : 0; const open = item.match(/\/images\/augslot\.jpg/) ? item.match(/\/images\/augslot\.jpg/g).length : 0; const atk = parseInt((item.replace(/(\([^)]*\)|<[^>]*>|,|\s)/g, '').match(/\+([0-9]+)ATK/i) || [0,0])[1]) const hp = parseInt((item.replace(/(\([^)]*\)|<[^>]*>|,|\s)/g, '').match(/\+([0-9]+)HP/i) || [0,0])[1]) const maxrage = parseInt((item.replace(/(\([^)]*\)|<[^>]*>|,|\s)/g, '').match(/\+([0-9]+)maxrage/i) || [0,0])[1]) // Add table row if the item isn't fully gemmed if (gems != 4){ const rarity = item.match(/color:#([A-Za-z0-9]+)"/i)[1]; const upgrades = await info("Cost to One Gem"); const cost = upgrades[rarity][gems]; tableData.push(` <tr id="row${iid}"> <td><a id="delete${iid}" href="javascript:void(0);">✘</a></td> <td><input type="checkbox" id="check${iid}"></td> <td><a href="blacksmith?itemid=${iid}&suid=${id}" target="_blank" id="iid${iid}" class="itemLink">${name}</a></td> <td>${gems}</td> <td>${augs+open}</td> <td><a href="augmentequip?suid=${id}" target="_blank">${open}</a></td> <td>${((atk+hp)*0.15/cost).toFixed(2)}</td> <td>${(maxrage*0.15/cost).toFixed(2)}</td> </tr> `); }; }; }; }; await Promise.all(charIdArray.map(inventory)); // Build table HTML document.querySelector("#itemStorageDiv").innerHTML = ` <table id="inventory" class="table table-striped sortable" style="text-align:left"> <thead><tr> <th><img src="images/questwiki.jpg" onmouseover="statspopup(event,'Click to delete the row')" onmouseout="kill()" style="margin-bottom:5px;"></th> <th><img src="images/questwiki.jpg" onmouseover="statspopup(event,'Mark completed. Will auto-check if you click on the item name')" onmouseout="kill()" style="margin-bottom:5px;"></th> <th>Item</th> <th>Gems</th> <th>Aug Slots</th> <th>Open Augs</th> <th>ATK+HP Gained per Point<br>with one gem added</th> <th>Max Rage gained per Point<br>with one gem added</th> </tr></thead> <tbody>${tableData.join('')}</tbody> </table> ` // Add functions const itemLinks = document.querySelectorAll(".itemLink") for (let i = 0; i < itemLinks.length; i++) { const link = itemLinks[i] const iid = link.outerHTML.match(/id="iid([0-9]+)"/)[1] document.querySelector(`#iid${iid}`).addEventListener('click',function(){ document.querySelector(`#check${iid}`).checked = true; }); document.querySelector(`#delete${iid}`).addEventListener('click',function(){ document.querySelector(`#row${iid}`).remove(); }); }; // Make table sortable sortableTables(); }; // App mob attacker async function appMobAttacker(server,serverNo,rgaName,charId){ // Styling GM_addStyle(` #mobAtkWidget {text-align:center;} .mob-div > input {width: 100px;font-size: 10px;padding: 5px;margin:5px;} .mob-div > img {height: 100px;width: 100px;border-radius: 15px;border:2px SOLID #313131;cursor: pointer;margin:5px;} .mob-div {display: inline-block;margin-bottom: 1rem;font-family: monospace;font-size: 12px;text-align: center;} #authSlider{display:none;} .mob-selected{border: 3px #00CC00 SOLID !important;padding: 3px !important;animation: mob-selected 1s infinite;} @keyframes mob-selected{0% {filter: saturate(100%);} 50% {filter: saturate(250%); } 100% {filter: saturate(100%);}} `) // HTML document.querySelector("#overlayWidget").innerHTML = ` <div id="mobAtkWidget"> <h4>CREATING BATTLE PLAN</h4><br> <img src="https://studiomoxxi.com/moxximod/loading-gif.gif" style="height:100px;width:100px"> </div> ` // Load chars from saved RGAs if (GM_getValue('savedRgas') != "no sessions saved" && GM_getValue('savedRgas')){ const rgas = GM_getValue('savedRgas').match(/rg_sess_id=[A-Za-z0-9]+&suid=[0-9]+&serverid=[0-9]+/g); await loadChars(rgas); // Load chars from current RGA if none saved } else { await loadChars([`rg_sess_id=${rgaName}&suid=${charId}&serverid=${serverNo}`]); }; // Convert each rga into an array of login strings for each char async function loadChars(rgas){ const strings = [] const parseChars = async (string) => { // Check if RGA is subscribed to MoxxiMod+ const session = string.match(/rg_sess_id=([A-Za-z0-9]+)/)[1]; const auth = await mmplus(`AuthCheck|${session}`); // Don't parse char data if not subscribed if (!auth.match("Full")){ return; }; // Parse char data const startingId = string.match(/suid=([0-9]+)/i)[1]; const ajax = await superfetch(`ajax/accounts.php?t_serv=${serverNo}&${string}`); const chars = ajax.match(/"id":"[0-9]+"/g).map(id => id.match(/[0-9]+/i)); chars.forEach(async char => { strings.push(string.replace(startingId,char)); }); }; await Promise.all(rgas.map(parseChars)); // Mobs objects (use quest:0 for mobs not associated with a quest or associated with more than 1 quest) let mobs = { "seeping":{charNames:[],charIds:[],quest:2092,id:4379,mobName:'SEEPING',roomNum:32876,mobImg:'images/mobs/chaosgolem5.jpg'}, "deluged":{charNames:[],charIds:[],quest:2093,id:4380,mobName:'DELUGED',roomNum:32878,mobImg:'images/mobs/chaosgolem2.jpg'}, "volatile":{charNames:[],charIds:[],quest:2094,id:4381,mobName:'VOLATILE',roomNum:32877,mobImg:'images/mobs/chaosgolem4.jpg'}, "lorren3":{charNames:[],charIds:[],quest:1532,id:3731,mobName:'PERPETUAL',roomNum:26616,mobImg:'images/mobs/mobc62.jpg'}, "aestor3":{charNames:[],charIds:[],quest:1891,id:3920,mobName:'ROENOV',roomNum:27528,mobImg:'images/mobs/mobm93.jpg'}, "aestor4":{charNames:[],charIds:[],quest:1916,id:3921,mobName:'SKITTOR',roomNum:27527,mobImg:'images/mobs/mobz2.jpg'}, "aestor5":{charNames:[],charIds:[],quest:1917,id:3922,mobName:'ERGON',roomNum:27526,mobImg:'images/mobs/mobi18.jpg'}, "ferocity":{charNames:[],charIds:[],quest:2035,id:4038,mobName:'ULKOR',roomNum:28122,mobImg:'images/mobs/mobc48.jpg'}, "class":{charNames:[],charIds:[],quest:2033,id:4035,mobName:'YOUNIS',roomNum:28113,mobImg:'images/mobs/mobk9.jpg'}, "hovok":{charNames:[],charIds:[],quest:0,id:4046,mobName:'HOVOK',roomNum:28123,mobImg:'images/mobs/mobm91.jpg'}, "rank5":{charNames:[],charIds:[],quest:1941,id:3931,mobName:'DARKLORD',roomNum:27608,mobImg:'images/mobs/moba76.jpg'}, "rank10":{charNames:[],charIds:[],quest:1942,id:3932,mobName:'FIREPRIEST',roomNum:27609,mobImg:'images/mobs/mobh85.jpg'}, "rank15":{charNames:[],charIds:[],quest:1943,id:3933,mobName:'ENCHANTER',roomNum:27632,mobImg:'images/mobs/mobi58.jpg'}, "rank20":{charNames:[],charIds:[],quest:1944,id:3934,mobName:'DIVINER',roomNum:27633,mobImg:'images/mobs/mobz78.jpg'}, "normok2":{charNames:[],charIds:[],quest:2047,id:4050,mobName:'CORVOK',roomNum:27624,mobImg:'images/mobs/mobd12.jpg'}, "invader":{charNames:[],charIds:[],quest:2359,id:5274,mobName:'INVADER',roomNum:37878,mobImg:'images/mobs/mobh31.png'}, "thief":{charNames:[],charIds:[],quest:0,id:3519,mobName:'THIEF',roomNum:26199,mobImg:'images/mobs/mobz59.jpg'}, "betrayer":{charNames:[],charIds:[],quest:0,id:3520,mobName:'BETRAYER',roomNum:26200,mobImg:'images/mobs/mobz41.jpg'}, "plant":{charNames:[],charIds:[],quest:0,id:4392,mobName:'PLANT',roomNum:31686,mobImg:'images/mobs/woodsplant.jpg'}, "gveiled":{charNames:[],charIds:[],quest:0,id:5301,mobName:'GUARD',roomNum:42619,mobImg:'images/mobs/veiledguard.png'}, "kveiled":{charNames:[],charIds:[],quest:0,id:5302,mobName:'KEEPER',roomNum:42620,mobImg:'images/mobs/veiledkeeper.png'}, "aprecision":{charNames:[],charIds:[],quest:0,id:5481,mobName:'PRECISION',roomNum:0,mobImg:'images/mobs/owmob784.png'}, "dprecision":{charNames:[],charIds:[],quest:0,id:5482,mobName:'PRECISION',roomNum:0,mobImg:'images/mobs/owmob176.png'}, "vprecision":{charNames:[],charIds:[],quest:0,id:5483,mobName:'PRECISION',roomNum:0,mobImg:'images/mobs/owmob1129.png'} }; // Arrays of chars to skip const circArray = []; const oorArray = []; // Loop through all login strings to populate data object async function scanChars(string){ // Fetch profile for each char const profileData = await superfetchProfile(`profile?${string}`); // Fetch key backpack for each char const kbp = await superfetch(`ajax/backpackcontents.php?tab=key&${string}`); const veiledteleporter = kbp.match(/data-name="Veiled Teleporter"/i); const veiledidol = kbp.match(/data-name="Veiled Idol"/i); const groveinsignia = kbp.match(/data-name="Grove Insignia"/i); // Parse variables const id = string.match(/rg_sess_id=([A-Za-z0-9]+)/i)[1] + '&' + string.match(/suid=([0-9]+)/i)[1]; const name = profileData.name; const rage = profileData.currentrage; const circ = profileData.skills.list.includes("Circumspect") ? "Yes" : "No" const faction = profileData.faction; const loyalty = profileData.loyalty; // Check if char has rage if (rage < 1000){ oorArray.push(name); // Loop each char through the mobs data object } else { for (const mobName of Object.keys(mobs)) { const mob = mobs[mobName]; // Fetch quest info if quest ID doesnt = 0 const quest = mob.quest == 0 ? "noquest" : await superfetch(`quest_info.php?questnum=${mob.quest}&${string}`); // Fetch to see if mobs are available for each quest or task if (quest != "error"){ // Hovok quests: Check all 4 maxed crests and if any of them don't error, check to see if Hovok is alive if (mobName == "hovok"){ const quantum = await superfetch(`quest_info.php?questnum=${2038}&${string}`); const explosive = await superfetch(`quest_info.php?questnum=${2039}&${string}`); const violent = await superfetch(`quest_info.php?questnum=${2040}&${string}`); const onslaught = await superfetch(`quest_info.php?questnum=${2041}&${string}`); if (quantum != "error" || explosive != "error" || violent != "error" || onslaught != "error"){ const search = await superfetch(`mob_search.php?target=${mob.id}&${string}`); if (search.match(/Quest help activated/i)){ mob.charNames.push(name); mob.charIds.push(id); }; }; // Veiled mobs: Check against key backpack keys } else if (mobName.match("veiled")){ if (mobName == "gveiled" && veiledteleporter){ const search = await superfetch(`mob_search.php?target=${mob.id}&${string}`); if (search.match(/Quest help activated/i)){ mob.charNames.push(name); mob.charIds.push(id); }; } else if (mobName == "kveiled" && veiledidol){ const search = await superfetch(`mob_search.php?target=${mob.id}&${string}`); if (search.match(/Quest help activated/i)){ mob.charNames.push(name); mob.charIds.push(id); }; }; // Tenacious mobs: Check against faction information } else if (mobName.match("precision")){ if (mobName == "aprecision" && faction == "Alvar" && loyalty >= 1 && groveinsignia){ const search = await superfetch(`mob_search.php?target=${mob.id}&${string}`); if (search.match(/Quest help activated/i)){ mob.charNames.push(name); mob.charIds.push(id); }; } else if (mobName == "dprecision" && faction == "Delruk" && loyalty >= 1 && groveinsignia){ const search = await superfetch(`mob_search.php?target=${mob.id}&${string}`); if (search.match(/Quest help activated/i)){ mob.charNames.push(name); mob.charIds.push(id); }; } else if (mobName == "vprecision" && faction == "Vordyn" && loyalty >= 1 && groveinsignia){ const search = await superfetch(`mob_search.php?target=${mob.id}&${string}`); if (search.match(/Quest help activated/i)){ mob.charNames.push(name); mob.charIds.push(id); }; }; // All other mobs } else { const search = await superfetch(`mob_search.php?target=${mob.id}&${string}`); if (search.match(/Quest help activated/i)){ mob.charNames.push(name); mob.charIds.push(id); }; }; }; }; }; }; await Promise.all(strings.map(scanChars)); // Build div elements to display on app const divs = []; if (oorArray.length > 0){ divs.push(`<div class="mob-div"><img src="images/rfury.jpg"><br>OUT OF RAGE<br>${oorArray.length} attackers<br><input type="text" class="form-control-new" value="${oorArray.join(',')}"></div>`); }; for (const mobName of Object.keys(mobs)) { const mob = mobs[mobName] if (mob.charIds.length > 0){ divs.push(` <div class="mob-div"> <img src="${mob.mobImg}" class="mob-img" name="${mob.mobName.toLowerCase()}" alt="${mob.roomNum}|${mob.charIds.join(',')}"><br> ${mob.mobName}<br> ${mob.charIds.length} attackers<br> <input type="text" class="form-control-new list-of-chars" value="${mob.charNames.join(',')}"> </div> `); }; }; // Widget HTML document.querySelector("#mobAtkWidget").innerHTML = ` <h4>CLICK MOB IMAGES TO SELECT TARGETS</h4> <div style="background-color:#000000;padding:10px;border-radius:10px;box-shadow:5px 5px 5px rgba(0, 0, 0, 1);margin-bottom:1rem;"> ${divs.join('')} </div> <button class="btn-mm" id="attackMobs">ATTACK SELECTED MOBS</button> ` // Select all text when textbox is clicked const textBoxes = document.querySelectorAll(".list-of-chars"); textBoxes.forEach(i => { i.addEventListener('click',function(){ i.select(); }); }); // Select mob when clicked const mobPics = document.querySelectorAll(".mob-img"); mobPics.forEach(i => { i.addEventListener('click',function(){ i.classList.toggle('mob-selected'); }); }); // Attack selected mobs button document.querySelector("#attackMobs").addEventListener('click',async function(){ const cmd = [`NewMobAtk|rganame|${server}|`]; const selectedMobs = document.querySelectorAll(".mob-selected"); selectedMobs.forEach(i => { const mobName = i.outerHTML.match(/name="([^"]*)"/i)[1]; const selectedChars = i.outerHTML.match(/alt="([^"]*)"/i)[1]; cmd.push(`${mobName}|${selectedChars.replace(/amp;/g,'')}`); }); document.querySelector("#attackMobs").remove(); await mmplus(cmd.join("\n")); GM_addStyle('#authSlider{display:revert !important;}'); }); }; }; // App gladiator attacker NEW async function appGladiator(server,serverNo,rgaName,charId){ GM_addStyle(` div.glad-app-box{box-shadow:5px 5px 5px rgba(0, 0, 0, 1);border-radius:10px !important;padding:20px !important;} img.glad-app-mob-img{height:250px;width:250px;margin:10px;border-radius:10px;box-shadow: 5px 5px 5px rgba(0, 0, 0, 1);transition: width 0.5s ease, height 0.5s ease;} #gladWorkspace{opacity:0;transition:opacity 0.5s ease;} #gladWorkspace > div{margin:10px;display:inline-block;vertical-align:top;} img.glad-despawned{filter:grayscale(100%);} img.glad-spawned{cursor:pointer;} td.my-glad-attacks{filter:invert(100%)} input.glad-txt{background:#FFFFFF !important;color:#000000 !important;opacity:1 !important;} #gladSkillsCast > table > tbody > tr > td > img{width: 30px;height: 30px;margin: 1px;border-radius: 5px;border: 2px #475254 SOLID;} #gladSkillsMissing > table > tbody > tr > td > img{width: 30px;height: 30px;margin: 1px;border-radius: 5px;border: 2px #475254 SOLID;} #rankingsOverflow{max-height:400px;overflow:auto;} `) const ajax = await superfetch('ajax_changeroomb'); const room = ajax.match(/"curRoom":"([0-9]+)"/i)[1]; if (room == "28031"){ const gladPage = await superfetch('gladiator'); const parser = new DOMParser(); const gladHtml = parser.parseFromString(gladPage, 'text/html'); const divQuest = gladHtml.querySelectorAll(".divQuest"); const gladMobs = []; divQuest.forEach(div => { const html = div.innerHTML; const status = html.match("Will retreat in") ? "glad-spawned" : "glad-despawned"; const imgSrc = html.match(/src="[^"]*"/i); const mobId = html.match(/mobid=[0-9]+/i); const mobName = html.match(/<h1>(.*?)<\/h1>/i)[1]; const img = `<img ${imgSrc} class="${status} glad-app-mob-img" alt="${mobId}" name="${mobName}">` gladMobs.push(img); }); document.querySelector("#overlayWidget").innerHTML = ` <div style="text-align:center"> ${gladMobs.join('')} <div id="gladWorkspace"> <div style="width:45%"> <div class="list-group-item glad-app-box" id="gladTools" style="margin-bottom:20px;">tools</div> <div class="list-group-item glad-app-box" id="gladSkillsCast" style="margin-bottom:20px;">skills cast</div> <div class="list-group-item glad-app-box" id="gladSkillsMissing" style="margin-bottom:20px;">skills missing</div> </div> <div style="width:45%"> <div class="list-group-item glad-app-box" id="gladRankings" style="margin-bottom:20px;"> rankings </div> </div> </div> </div> ` // Glad image button clicks const allGlads = document.querySelectorAll(".glad-app-mob-img"); const aliveGlads = document.querySelectorAll(".glad-spawned"); aliveGlads.forEach(img => { img.addEventListener('click', async function(){ const imgData = img.outerHTML; // Glad name and inputs const mobName = imgData.match(/name="([^"]*)"/i)[1]; document.querySelector("#gladTools").innerHTML = ` Current Rage: <span id="gladRage">loading</span><br> <h2>${mobName}</h2> Stop after <input id="stopAttacks" type="number" class="form-control-new glad-txt" style="width:100px;" autocomplete="off"> attacks or <input id="stopDamage" type="number" class="form-control-new glad-txt" autocomplete="off" style="width:200px;"> damage <br> <button id="atkGlad" class="btn-mm" style="margin-top:20px;">Attack ${mobName}</button> ` // Populate rankings const mobId = imgData.match(/alt="mobid=([0-9]+)"/i)[1]; await appGladiatorGetRankings(mobId,charId); // Cast skills and potions const profileData = await superfetchProfile('profile'); document.querySelector("#gladSkillsCast").innerHTML = `<table class="table table-striped"><tr><td><h6>Active Skills & Potions</h6>${profileData.skills.images.join('')}</tr></td></table>` const castPotionsList = profileData.skills.list; const allOwPotions = await info("All Potions"); const missingPotions = []; allOwPotions.forEach(pot => { const potName = pot[0]; const potImg = pot[1]; if (!castPotionsList.includes(potName)){ missingPotions.push(`<img src="${potImg}" onmouseover="statspopup(event,'<b>${potName}<b>')" onmouseout="kill()">`) }; }); document.querySelector("#gladSkillsMissing").innerHTML = `<table class="table table-striped"><tr><td><h6>Missing Potions</h6>${missingPotions.join('')}</tr></td></table>` // Attack glad button document.querySelector("#atkGlad").addEventListener('click', async function(){ const stopAttacks = document.querySelector("#stopAttacks").value.replace(/,/g,''); const stopDamage = document.querySelector("#stopDamage").value.replace(/,/g,''); if (isNaN(stopAttacks) || isNaN(stopDamage) || stopAttacks == '' || stopDamage == ''){ alert('ERROR: Please enter target amounts for number of attacks and damage'); } else { const regex = new RegExp(`"name":"${mobName}","level":"[0-9]+","rage":"[0-9]+","h":"[^"]*","encid":"[^"]*","mobId":"[0-9]+","spawnId":"[0-9]+","image":"[^"]*"`,'i'); const string = ajax.match(regex).toString(); const spawnId = string.match(/"spawnId":"([0-9]+)"/)[1] await mmplus(`MultiMobAtk|rganame|${server}|${charId}|${stopAttacks}|${spawnId}|${stopDamage}`); }; }); // Rage refresher setInterval(async () => { const userstats = await superfetch('userstats',true); const rage = userstats.match(/"rage":"([^"]*)"/i) ? userstats.match(/"rage":"([^"]*)"/i)[1] : "Error"; document.querySelector("#gladRage").innerHTML = rage; }, 500); // Resize glad app icons allGlads.forEach(resize => { resize.style.width = '50px'; resize.style.height = '50px'; }); document.querySelector("#gladWorkspace").style.opacity = "1"; }); }); } else { // Prompt to move to gladiator room 28031 document.querySelector("#overlayWidget").innerHTML = `<center><h3>Must be in the Gladiator Arena</h3><hr><button class="btn-mm" id="goTo28031">GO TO RM 28031</button></center>` document.querySelector("#goTo28031").addEventListener('click', async function(){ await goToRoomNum(server,charId,28031); }); }; }; // Load rankings for gladiator async function appGladiatorGetRankings(mobId,charId){ const gladPageTxt = await superfetch(`gladiator?mobid=${mobId}`,true); const parser = new DOMParser(); const gladPageHtml = parser.parseFromString(gladPageTxt, 'text/html'); const divs = Array.from(gladPageHtml.querySelectorAll('.mt-lg-0 .grid-item')).map(div => div.innerHTML); let columns = divs.indexOf("Atks") + 1; // Build array of arrays for each line in the rankings table const rankArrays = []; let tempArray = []; divs.forEach((div, index) => { tempArray.push(div); // Columns is the number of columns to create for the table based on the index value of "Atks" if ((index + 1) % (columns) === 0) { rankArrays.push(tempArray); tempArray = []; } }); // Build table rows from rankings array const rows = []; rankArrays.forEach(col => { let row = '' for (let i = 0; i < columns; i++) { if (col.toString().match(`id=${charId}`)){ row += `<td class="my-glad-attacks">${col[i]}</td>` } else { row += `<td>${col[i]}</td>` } }; rows.push(`<tr>${row}</tr>`); }); document.querySelector("#gladRankings").innerHTML = ` <div id="rankingsOverflow"> <table id="rankingsTable" class="table table-striped"> ${rows.join('').replace(/<a.*?>/g,'')} </table> </div><br> <button id="resfreshRankings" class="btn-mm" style="margin-bottom:10px;">Refresh Rankings</button> ` // Auto-scroll to my glad attacks let targetRow = document.querySelector(".my-glad-attacks"); if (targetRow) { targetRow.scrollIntoView({ behavior: "smooth", block: "center" }); } // Refresh rankings button function document.querySelector("#resfreshRankings").addEventListener('click', async function(){ await appGladiatorGetRankings(mobId,charId); }); }; // App prime raider async function appPrimeRaider(server,serverNo,rgaName,charId){ // Styling GM_addStyle(` div.list-group-item{text-align:left;overflow:auto;height:90%;display:inline-block;margin:0.4%;padding:20px;border-radius:10px !important;box-shadow: 5px 5px 5px rgba(0, 0, 0, 1);} div.mobImgDiv {display: inline-block;font-size: 11px;text-align: center;font-family: monospace,monospace;height: 180px;width: 145px;margin-left:3px;margin-right:3px;margin-bottom:20px;vertical-align:top;} img.mobImg {box-shadow: 5px 5px 5px rgba(0, 0, 0, 1);cursor: pointer;width: 125px;height: 125px;border-radius: 5px;margin-bottom: 5px;transition: 0.25s ease-out;} .mob-selected{border: 4px #00CC00 SOLID !important;width: 125px !important;height: 125px !important;padding: 3px !important;} #topDiv {text-align:center;height:50%;overflow:auto;} #bottomDiv{overflow:auto;height:50%;width:100%;text-align:center;} button.btn-mm{font-size:12px;padding:0.5rem;} #selectedRaidMob{animation: tileanimate 2s infinite;box-shadow: 0 0 3px rgba(0, 0, 0, 0.5);} @keyframes tileanimate{0% {filter: saturate(100%);} 50% {filter: saturate(250%); } 100% {filter: saturate(100%);}} `); // Build HTML content document.querySelector("#overlayWidget").innerHTML = ` <div id="topDiv"> <div class="list-group-item" style="width:18%"> <h6>SELECTED MOBS</h6> <span id="mobsList">None</span> <hr> Characters required: <span id="minCount">0</span><br> Characters selected: <span id="charCount">0</span><hr> <button class="btn-mm" id="raidMob">RAID MOBS</button> <button class="btn-mm" id="saveGroup">SAVE GROUP</button> </div> <div class="list-group-item" style="width:18%"> <h6>SELECTED CHARACTERS</h6> <span id="nameList" style="overflow-wrap:break-word"></span> </div> <div class="list-group-item" style="width:38%" id="charactersDiv"> <center> <span id="availableCharacters"><img src="https://studiomoxxi.com/moxximod/loading-gif.gif" style="height:80px;width:80px;margin-top:1rem;"></span> </div> <div class="list-group-item" style="width:18%"> <center> <span id="groupList"><img src="https://studiomoxxi.com/moxximod/loading-gif.gif" style="height:80px;width:80px;margin-top:1rem;"></span> </div> </div> <div class="innerDivs" id="bottomDiv"> <img src="https://studiomoxxi.com/moxximod/loading-gif.gif" style="height:80px;width:80px;margin-top:1rem;"> </div> ` // Fetch available prime gods const primegods = await superfetch('primegods'); const parser = new DOMParser(); const primehtml = parser.parseFromString(primegods, 'text/html'); const alivegods = primehtml.querySelectorAll('span.mobbox:not(.grey)'); const alivegodsarray = []; for (var i = 0; i < alivegods.length; i++){ const god = alivegods[i]; const name = god.innerHTML.match(/event,'([^']*)'/i)[1]; const img = god.innerHTML.match(/src="([^"]*)"/i)[1]; const time = ((god.innerHTML.match(/style="([^"]*)"/i)[1].toString().match(/width:\s*([\d.]+)%/)[1])/100*23).toFixed(1); const link = god.innerHTML.match(/href="([^"]*)"/i)[1]; const page = await superfetch(link); const min = page.match(/Max Members: ([0-9]+)/i)[1]; alivegodsarray.push(`<div class="mobImgDiv"><img id="${name.replace(/,/g,'')}" alt="${min}" class="mobImg" src="${img}"><br><b>${name.replace(/,/g,'')}</b><br>${time} hours</div>`) } document.querySelector("#bottomDiv").innerHTML = alivegodsarray.join(''); // On-click mob image buttons const mobs = document.querySelectorAll('.mobImg'); for (var i = 0; i < mobs.length; i++){ mobs[i].addEventListener('click', async function(){ const selectedMobs = []; const minJoiners = [0]; this.classList.toggle('mob-selected'); const selected = document.querySelectorAll('.mob-selected'); selected.forEach(mob => { selectedMobs.push(mob.outerHTML.match(/id="([^"]*)"/i)[1]); minJoiners.push(parseInt(mob.outerHTML.match(/alt="([^"]*)"/i)[1])); }); document.querySelector("#mobsList").innerHTML = selectedMobs.join(', '); const minMax = Math.max(...minJoiners); document.querySelector("#minCount").innerHTML = minMax }); }; // Get profile of current character and parse crew name const profile = await superfetch(`profile?id=${charId}`); const crew = (profile.match(/<a href="(\/crew_profile\?id=[0-9]+)">(.*?)<\/a>/i) || ['','','']); const crewpro = await superfetch(crew[1]); const crewname = crew[2]; const crewpic = crewpro.match(/https:\/\/upload\.outwar\.com\/crewuploaded\/[A-Za-z][0-9]+\.[A-Za-z]+/i) || "images/logodefault.gif"; // Set characters div background image var charactersDiv = document.querySelector("#charactersDiv"); charactersDiv.style.backgroundImage = `url('${crewpic}')`; charactersDiv.style.backgroundSize = "cover"; // or "contain" or other value charactersDiv.style.backgroundRepeat = "no-repeat"; charactersDiv.style.backgroundPosition = "center"; // Get all accessible chars from the same crew const myaccount = await superfetch('myaccount'); const capstatus = await superfetch('crew_capstatus'); const regex = new RegExp(`<atarget="_top"href="[^"]*">[\\w\\d\\s]*<\\/a><\\/td><td>[0-9]+<\\/td><td>${crewname.replace(/\s+/g,'')}<\\/td>`,'g') const chars = myaccount.replace(/[\n\r]/g,'').replace(/ style="text-align: center;"/g,'').replace(/ <a href="[^"]*"><img border="0" height="13" src="[^"]*" alt="[A-Za-z]+"><\/a>/g,'').replace(/\s+/g,'').match(regex); const rows = []; for (var c = 0; c < chars.length; c++){ const char = chars[c] const id = char.match(/suid=([0-9]+)/i)[1]; const name = char.match(/<atarget="_top"href="[^"]*">(.*?)<\/a>/i)[1]; const check = `<input type="checkbox" class="checkbox" name="${name}" id="${id}">`; const capregex = new RegExp(`<td>${name}<\/td><td>[0-9]+<\/td>`); const cap = 10 - parseInt((capstatus.match(capregex) || '<td>0</td>').toString().match(/<td>([0-9]+)<\/td>/)[1]); rows.push(`<tr><td>${check}</td><td>${name}</td><td>${cap}</td></tr>`); }; document.querySelector("#availableCharacters").innerHTML = ` <table class="table sortable"> <thead><tr><th></th><th><img src="https://torax.outwar.com/images/questwiki.jpg" style="height:12px;width:12px;margin-top:-3px;margin-right:2px;" onmouseover="statspopup(event,'All characters on this RGA or trusteed to this RGA who share the same crew as the active character.')" onmouseout="kill()"> AVAILABLE CHARACTERS</th><th>AVAILABLE CAPS</th></tr></thead> <tbody>${rows.join('')}</tbody></table>` // Load saved groups await loadSavedGroups(); // Load saved groups function (created function so it can be called when creating a new group) async function loadSavedGroups(){ // Get saved groups const groups = []; let count = 0 // Loop through all n values to find stored values for (let i = 0; i < 10000000; i++) { const value = GM_getValue(`mobRaiderGroup(${i})`); if (value){ count++ // Parse group data from values const groupname = value.split(',').slice(0, 1); const groupchars = value.split(',').slice(1); // Create row HTML const html = ` <tr id="groupRow${i}"> <td><a href="javascript:void(0);" id="group${i}" alt="${groupchars}" class="select-group">${groupname}</a></td> <td><a href="javascript:void(0);" id="group${i}" class="delete-group">[del]</a></td> </tr>` groups.push(html); } else { // Break out of loop once group number isn't found break; }; }; // Add saved groups table document.querySelector("#groupList").innerHTML = `<h6>SAVED GROUPS</h6><table class="table"><tbody>${groups.join('')}</tbody></table>` // Saved groups table link functions const sel = document.querySelectorAll('.select-group') const del = document.querySelectorAll('.delete-group') for (let i = 0; i < sel.length; i++) { // Select group function sel[i].addEventListener('click', async function(){ // Uncheck all boxes document.querySelectorAll('.checkbox').forEach(checkbox => checkbox.checked = false); // Parse char ID array from group data const charIds = (this.outerHTML.match(/alt="([^"]*)"/i)[1]).split(','); charIds.forEach(async id => { var checkbox = document.getElementById(id); if (checkbox) checkbox.checked = true; await updateSelectedChars(); }); }); // Delete group function del[i].addEventListener('click', async function(){ const num = this.outerHTML.match(/id="group([0-9]+)"/i)[1] GM_deleteValue(`mobRaiderGroup(${num})`); document.querySelector(`#groupRow${num}`).remove(); }); }; }; // Checkbox on-check function call const boxes = document.querySelectorAll('.checkbox') for (let i = 0; i < boxes.length; i++) { boxes[i].addEventListener('click', async function(){ await updateSelectedChars(); }); }; // Update selected chars list function async function updateSelectedChars(){ const allChecked = document.querySelectorAll('.checkbox:checked') document.querySelector("#charCount").innerHTML = allChecked.length; const nameList = (Array.from(allChecked).map(node => node.outerHTML.match(/name="([^"]*)"/i)[1])).join(','); document.querySelector("#nameList").innerHTML = nameList; }; // Save group button document.querySelector("#saveGroup").addEventListener('click', async function(){ // Alert error if no chars selected if (document.querySelector("#charCount").innerHTML == "0"){ alert('Please select characters before trying to save a group'); return; }; // Prompt for name input var name = prompt("Enter a name for the group"); // Alert for no name added if (name == ''){ alert ('You did not enter a name. The group was not saved'); // Save group if name was added } else { const allChecked = document.querySelectorAll('.checkbox:checked'); const idList = (Array.from(allChecked).map(node => node.outerHTML.match(/id="([0-9]+)"/i)[1])).join(','); const listName = name.replace(/ /g,'').replace(/,/g,''); for (let i = 0; i < 10000000; i++) { const value = GM_getValue(`mobRaiderGroup(${i})`); if (!value){ GM_setValue(`mobRaiderGroup(${(i)})`, listName + ',' + idList); await loadSavedGroups(); break; }; }; }; }); // Raid mob function document.querySelector("#raidMob").addEventListener('click', async function(){ // Check to make sure the right number of chars have been selected const reqChars = parseInt(document.querySelector("#minCount").innerHTML) const selChars = parseInt(document.querySelector("#charCount").innerHTML) if (selChars >= reqChars){ // Get the values for all selected mobs const allChecked = document.querySelectorAll('.checkbox:checked'); const idList = (Array.from(allChecked).map(node => node.outerHTML.match(/id="([^"]*)"/i)[1])).join(','); const mobs = []; const selected = document.querySelectorAll('.mob-selected'); // Loop for each selected mob selected.forEach(mob => { mobs.push(mob.outerHTML.match(/id="([^"]*)"/i)[1]); // Clear selected mobs mob.classList.toggle('mob-selected'); document.querySelector("#mobsList").innerHTML = 'None'; }); // Fire raid function await mmplus(`PrimeRaid|rganame|${server}|${mobs.join(',')}|${idList}`); } else { alert(`Please select ${reqChars} characters to raid with`); }; }); await sortableTables(); }; // App mob raider async function appMobRaider(server,serverNo,rgaName,charId){ // Styling GM_addStyle(` div.mobImgDiv {display: inline-block;font-size: 10px;text-align: center;font-family: monospace,monospace;height: 105px;width: 100px;margin-left:3px;margin-right:3px;margin-bottom:20px;} img.mobImg {box-shadow: 5px 5px 5px rgba(0, 0, 0, 1);cursor: pointer;width: 95px;height: 95px;border-radius: 5px;margin-bottom: 5px;transition: 0.25s ease-out;} .mob-selected{border: 4px #00CC00 SOLID !important;width: 95px !important;height: 95px !important;padding: 3px !important;} div.list-group-item {text-align:left;overflow:auto;height:95%;display:inline-block;margin:0.4%;padding:20px;border-radius:10px !important;box-shadow: 5px 5px 5px rgba(0, 0, 0, 1);width:23%;} #topDiv {text-align:center;height:50%;overflow:auto;} #bottomDiv{overflow:auto;height:50%;width:100%;padding:10px;text-align:center;} button.btn-mm{font-size:12px;padding:0.5rem;} #selectedRaidMob{animation: tileanimate 2s infinite;box-shadow: 0 0 3px rgba(0, 0, 0, 0.5);} @keyframes tileanimate{0% {filter: saturate(100%);} 50% {filter: saturate(250%); } 100% {filter: saturate(100%);}} `); // Build HTML content document.querySelector("#overlayWidget").innerHTML = ` <div id="topDiv"> <div class="list-group-item"> <h6>SELECTED MOBS</h6> <span id="mobsList">None</span> <hr> Characters required: <span id="minCount">0</span><br> Characters selected: <span id="charCount">0</span><hr> <button class="btn-mm" id="raidMob">RAID MOBS</button> <button class="btn-mm" id="saveGroup">SAVE GROUP</button> </div> <div class="list-group-item"> <h6>SELECTED CHARACTERS</h6> <span id="nameList" style="overflow-wrap:break-word"></span> </div> <div class="list-group-item"> <h6>AVAILABLE CHARACTERS</h6> <span id="availableCharacters"><img src="https://studiomoxxi.com/moxximod/loading-gif.gif" height="80px" width="80px"></span> </div> <div class="list-group-item"> <h6>SAVED GROUPS</h6> <span id="groupList"></span> </div> </div> <div class="innerDivs" id="bottomDiv"> <div class="mobImgDiv"><img id="veiled" alt="3" class="mobImg" src="/images/mobs/veiledchest.png">Locker</div> <div class="mobImgDiv"><img id="morrik" alt="10" class="mobImg" src="images/mobs/mobi57.jpg">Morrik</div> <div class="mobImgDiv"><img id="grouz" alt="10" class="mobImg" src="images/mobs/moba56.jpg">Grouz</div> <div class="mobImgDiv"><img id="crawling" alt="1" class="mobImg" src="images/mobs/arsetmob1.gif">Crawling</div> <div class="mobImgDiv"><img id="conductor" alt="30" class="mobImg" src="images/mobs/mobk50.jpg">Conductor</div> <div class="mobImgDiv"><img id="demonic" alt="30" class="mobImg" src="images/mobs/mobl83.jpg"><br>Demonic</div> <div class="mobImgDiv"><img id="elexocutioner" alt="30" class="mobImg" src="images/mobs/moba87.jpg">Elexocutioner</div> <div class="mobImgDiv"><img id="chaosele" alt="5" class="mobImg" src="images/mobs/chaosgolem3.jpg">Chaotic</div> <div class="mobImgDiv"><img id="badge1" alt="10" class="mobImg" src="images/mobs/mobc86.jpg">Sharkrat</div> <div class="mobImgDiv"><img id="badge2" alt="10" class="mobImg" src="images/mobs/mobl29.jpg">Vullianne</div> <div class="mobImgDiv"><img id="badge3" alt="10" class="mobImg" src="images/mobs/mobd23.jpg">Kaggar</div> <div class="mobImgDiv"><img id="badge4" alt="10" class="mobImg" src="images/mobs/mobzz49.gif">Cheigon</div> <div class="mobImgDiv"><img id="badge5" alt="10" class="mobImg" src="images/mobs/mobg13.jpg">Zyssi</div> <div class="mobImgDiv"><img id="eth1" alt="5" class="mobImg" src="images/mobs/arraid1.jpg">Priest</div> <div class="mobImgDiv"><img id="eth2" alt="5" class="mobImg" src="images/mobs/arsetraid2.jpg">Serpent</div> <div class="mobImgDiv"><img id="eth3" alt="5" class="mobImg" src="images/mobs/arraid3.jpg">Steed</div> <div class="mobImgDiv"><img id="eth4" alt="5" class="mobImg" src="images/mobs/arraid4.jpg">Sorcerer</div> <div class="mobImgDiv"><img id="eth5" alt="5" class="mobImg" src="images/mobs/arraid5.jpg">Doomlord</div> <div class="mobImgDiv"><img id="novice" alt="5" class="mobImg" src="images/mobs/mobf28.jpg">Novice</div> <div class="mobImgDiv"><img id="great" alt="5" class="mobImg" src="images/mobs/mobb10.jpg">Great</div> <div class="mobImgDiv"><img id="grand" alt="5" class="mobImg" src="images/mobs/mobe57.jpg">Grand</div> <div class="mobImgDiv"><img id="rival1" alt="5" class="mobImg" src="images/mobs/pathraid1.gif">1 Rival</div> <div class="mobImgDiv"><img id="rival2" alt="5" class="mobImg" src="images/mobs/pathraid2.gif">2 Rival</div> <div class="mobImgDiv"><img id="rival3" alt="5" class="mobImg" src="images/mobs/pathraid3.gif">3 Rival</div> <div class="mobImgDiv"><img id="rival4" alt="5" class="mobImg" src="images/mobs/pathraid4.jpg">4 Rival</div> <div class="mobImgDiv"><img id="rival5" alt="5" class="mobImg" src="images/mobs/pathraid5.jpg">5 Rival</div> <div class="mobImgDiv"><img id="rival6" alt="5" class="mobImg" src="images/mobs/pathraid6.gif">6 Rival</div> <div class="mobImgDiv"><img id="rival7" alt="5" class="mobImg" src="images/mobs/pathraid7.jpg">7 Rival</div> <div class="mobImgDiv"><img id="rival8" alt="5" class="mobImg" src="images/mobs/pathraid8.jpg">8 Rival</div> <div class="mobImgDiv"><img id="rival9" alt="5" class="mobImg" src="images/mobs/pathraid9.gif">9 Rival</div> <div class="mobImgDiv"><img id="rival10" alt="5" class="mobImg" src="images/mobs/pathraid10.gif">10 Rival</div> </div> ` // On-click mob image buttons const mobs = document.querySelectorAll('.mobImg'); for (var i = 0; i < mobs.length; i++){ mobs[i].addEventListener('click', async function(){ const selectedMobs = []; const minJoiners = [0]; this.classList.toggle('mob-selected'); const selected = document.querySelectorAll('.mob-selected'); selected.forEach(mob => { selectedMobs.push(mob.outerHTML.match(/id="([^"]*)"/i)[1]); minJoiners.push(parseInt(mob.outerHTML.match(/alt="([^"]*)"/i)[1])); }); document.querySelector("#mobsList").innerHTML = selectedMobs.join(', '); const minMax = Math.max(...minJoiners); document.querySelector("#minCount").innerHTML = minMax }); }; // Get profile of current character and parse crew name const profile = await superfetch(`profile?id=${charId}`); const crew = (profile.match(/<a href="\/crew_profile\?id=[0-9]+">(.*?)<\/a>/i) || ['',''])[1]; // Get all accessible chars from the same crew const myaccount = await superfetch('myaccount'); const regex = new RegExp(`<atarget="_top"href="[^"]*">[\\w\\d\\s]*<\\/a><\\/td><td>[0-9]+<\\/td><td>${crew.replace(/\s+/g,'')}<\\/td>`,'g') const chars = myaccount.replace(/[\n\r]/g,'').replace(/ style="text-align: center;"/g,'').replace(/ <a href="[^"]*"><img border="0" height="13" src="[^"]*" alt="[A-Za-z]+"><\/a>/g,'').replace(/\s+/g,'').match(regex); const rows = []; for (var c = 0; c < chars.length; c++){ const char = chars[c] const id = char.match(/suid=([0-9]+)/i)[1]; const name = char.match(/<atarget="_top"href="[^"]*">(.*?)<\/a>/i)[1]; const check = `<input type="checkbox" class="checkbox" name="${name}" id="${id}">`; rows.push(`<tr><td>${check}</td><td>${name}</td></tr>`); }; document.querySelector("#availableCharacters").innerHTML = `<table class="table"><tbody>${rows.join('')}</tbody></table>` // Load saved groups await loadSavedGroups(); // Load saved groups function (created function so it can be called when creating a new group) async function loadSavedGroups(){ // Get saved groups const groups = []; let count = 0 // Loop through all n values to find stored values for (let i = 0; i < 10000000; i++) { const value = GM_getValue(`mobRaiderGroup(${i})`); if (value){ count++ // Parse group data from values const groupname = value.split(',').slice(0, 1); const groupchars = value.split(',').slice(1); // Create row HTML const html = ` <tr id="groupRow${i}"> <td><a href="javascript:void(0);" id="group${i}" alt="${groupchars}" class="select-group">${groupname}</a></td> <td><a href="javascript:void(0);" id="group${i}" class="delete-group">[del]</a></td> </tr>` groups.push(html); } else { // Break out of loop once group number isn't found break; }; }; // Add saved groups table document.querySelector("#groupList").innerHTML = `<table class="table"><tbody>${groups.join('')}</tbody></table>` // Saved groups table link functions const sel = document.querySelectorAll('.select-group') const del = document.querySelectorAll('.delete-group') for (let i = 0; i < sel.length; i++) { // Select group function sel[i].addEventListener('click', async function(){ // Uncheck all boxes document.querySelectorAll('.checkbox').forEach(checkbox => checkbox.checked = false); // Parse char ID array from group data const charIds = (this.outerHTML.match(/alt="([^"]*)"/i)[1]).split(','); charIds.forEach(async id => { var checkbox = document.getElementById(id); if (checkbox) checkbox.checked = true; await updateSelectedChars(); }); }); // Delete group function del[i].addEventListener('click', async function(){ const num = this.outerHTML.match(/id="group([0-9]+)"/i)[1] GM_deleteValue(`mobRaiderGroup(${num})`); document.querySelector(`#groupRow${num}`).remove(); }); }; }; // Checkbox on-check function call const boxes = document.querySelectorAll('.checkbox') for (let i = 0; i < boxes.length; i++) { boxes[i].addEventListener('click', async function(){ await updateSelectedChars(); }); }; // Update selected chars list function async function updateSelectedChars(){ const allChecked = document.querySelectorAll('.checkbox:checked') document.querySelector("#charCount").innerHTML = allChecked.length; const nameList = (Array.from(allChecked).map(node => node.outerHTML.match(/name="([^"]*)"/i)[1])).join(','); document.querySelector("#nameList").innerHTML = nameList; }; // Save group button document.querySelector("#saveGroup").addEventListener('click', async function(){ // Alert error if no chars selected if (document.querySelector("#charCount").innerHTML == "0"){ alert('Please select characters before trying to save a group'); return; }; // Prompt for name input var name = prompt("Enter a name for the group"); // Alert for no name added if (name == ''){ alert ('You did not enter a name. The group was not saved'); // Save group if name was added } else { const allChecked = document.querySelectorAll('.checkbox:checked'); const idList = (Array.from(allChecked).map(node => node.outerHTML.match(/id="([0-9]+)"/i)[1])).join(','); const listName = name.replace(/ /g,'').replace(/,/g,''); for (let i = 0; i < 10000000; i++) { const value = GM_getValue(`mobRaiderGroup(${i})`); if (!value){ GM_setValue(`mobRaiderGroup(${(i)})`, listName + ',' + idList); await loadSavedGroups(); break; }; }; }; }); // Raid mob function document.querySelector("#raidMob").addEventListener('click', async function(){ // Check to make sure the right number of chars have been selected const reqChars = parseInt(document.querySelector("#minCount").innerHTML) const selChars = parseInt(document.querySelector("#charCount").innerHTML) if (selChars >= reqChars){ // Get the values for all selected mobs const allChecked = document.querySelectorAll('.checkbox:checked'); const idList = (Array.from(allChecked).map(node => node.outerHTML.match(/id="([^"]*)"/i)[1])).join(','); const mobs = []; const selected = document.querySelectorAll('.mob-selected'); // Loop for each selected mob selected.forEach(mob => { mobs.push(mob.outerHTML.match(/id="([^"]*)"/i)[1]); // Clear selected mobs mob.classList.toggle('mob-selected'); document.querySelector("#mobsList").innerHTML = 'None'; }); // Fire raid function await mmplus(`RaidMob|rganame|${server}|${mobs.join(',')}|${idList}`); } else { alert(`Please select ${reqChars} characters to raid with`); }; }); }; // Caster menu async function casterMenu(rgaName,charId,serverNo,profileData){ travelMenuClose(); appsMenuClose(); searchMenuClose(); // Toggle const menu = document.querySelector("#btnCaster"); if (menu.classList.contains("menu-open")) { menu.classList.add("menu-close"); menu.classList.remove("menu-open"); await casterMenuClose(); } else { await casterMenuOpen(rgaName,charId,serverNo,profileData); menu.classList.add("menu-open"); menu.classList.remove("menu-close"); }; }; // Opens the quick cast menu async function casterMenuOpen(rgaName,charId,serverNo,profileData) { const server = serverNo == "1" ? "sigil" : "torax"; // Rotate quick caster image document.querySelector("#btnCaster > img").classList.toggle('rotate180'); GM_addStyle (`#btnCaster > img {transition: transform 0.5s ease;}`); GM_addStyle (`.rotate180 {transform: rotate(180deg);}`); // Build moxximod caster menu const allCast = profileData.skills.list; // Get potion data let potsArray = []; // Check if point potion boosters are cast and if not, add to the available list if (!allCast.match("Boost One")){ potsArray.push(`<img src="images/items/icon_vial_blue.jpg" class="castable cast-potion" onmouseover="statspopup(event,'<b>Cast all 5 booster potions</b><br>Cost: 25 points')" onmouseout="kill()" id="boosterPotions">`); }; // Fetch from backpack const potionBp = await superfetch('ajax/backpackcontents.php?tab=potion'); const allPots = potionBp.match(/data-iid="[0-9]+" id="[^"]*" class="[^"]*" src="[^"]*" alt="[^"]*"/g); for (let i = 0; i < allPots.length; i++) { const potion = allPots[i] const potId = potion.match(/data-iid="([0-9]+)"/i)[1]; const potImg = potion.match(/src="([^"]*)"/i)[1]; const potAlt = potion.match(/alt="([^"]*)"/i)[1]; if (!allCast.includes(potAlt)){ potsArray.push(`<img src="${potImg}" class="castable cast-potion" id="${potId}" onmouseover="itempopup(event,'${potId}')" onmouseout="kill()">`); }; }; // Get skill data let skillsArray = []; let skillIdArray = []; const classEndpoints = ['cast_skills.php','cast_skills.php?C=4','cast_skills.php?C=5','cast_skills.php?C=6','cast_skills.php?C=7'] const skillClasses = await Promise.all(classEndpoints.map(endpoint => fetch(endpoint).then(res => res.text()))); for (let i = 0; i < skillClasses.length; i++) { const skillIds = skillClasses[i].match(/loadskill\([0-9]+\)/g) || []; for (let i = 0; i < skillIds.length; i++) { skillIdArray.push(skillIds[i].match(/[0-9]+/i)) }; }; const skillInfos = await Promise.all(skillIdArray.map(endpoint => fetch(`skills_info.php?id=${endpoint}`).then(res => res.text()))); for (let i = 0; i < skillInfos.length; i++) { const info = skillInfos[i] if (info.match(`<input type="submit" name="cast" class="btn btn-primary" value="Cast Skill">`)){ const skillName = info.match(/<h5>(.*?)<\/h5>/i)[1] const skillImg = info.match(/style="[^"]*" src="([^"]*)"/i)[1] const skillId = info.match(/value="([0-9]+)"/i)[1] const skillText = info.replace(/[\n\r]/g,'').replace(/'/g,'').match(/<h5>(.*?)<\/div>/)[1]; if (!allCast.includes(skillName)){ skillsArray.push(`<img src="${skillImg}" class="castable cast-skill" id="${skillId}" onmouseover="statspopup(event,'<b>${skillName}</b><br>${skillText}')" onmouseout="kill()">`) }; }; }; const casterHtml = ` <div id="casterSkills">${skillsArray.join(' ')}</div><p> <div id="casterPotions">${potsArray.join(' ')}</div> <p style="margin-top:1rem;"> <center> <button class="btn-mm" id="casterCastAll" style="background-color:#480000">Cast selected</button> <button class="btn-mm" id="casterSelectAll">Select all skills</button> <button class="btn-mm" id="casterDeselectAll">Deselect all skills</button> <p style="margin-top:1rem;"> <select class="btn-mm" id="casterPresetsDrop"><option value="" disabled="" selected="" hidden="">Select preset</option></select> <button class="btn-mm" id="saveAsPreset">Save preset</button> <button class="btn-mm" id="deletePreset">Delete preset</button> ` // Create div var image = document.querySelector("#btnCaster").getBoundingClientRect(); var newDiv = document.createElement('div'); newDiv.id = 'casterDiv'; newDiv.classList.toggle('widget'); newDiv.innerHTML = casterHtml; newDiv.style.position = 'absolute'; newDiv.style.top = image.top + window.scrollY + 'px'; newDiv.style.left = image.left + window.scrollX-650 + 'px'; document.body.appendChild(newDiv); // Click potion or skills to toggle class document.getElementById('casterDiv').addEventListener('click', function(event) { if (event.target.tagName.toLowerCase() === 'img') { const imgElement = event.target; if (imgElement.id === 'boosterPotions' && !imgElement.classList.contains('caster-selected')) { const confirmBoost = confirm('Are you sure you want to cast booster potions for 25 points?'); if (confirmBoost) { imgElement.classList.toggle('caster-selected'); } else { return; } } else { imgElement.classList.toggle('caster-selected'); } } }); // Button functions document.querySelector("#casterSelectAll").addEventListener('click', function() { $("#casterSkills > img").addClass("caster-selected"); }); document.querySelector("#casterDeselectAll").addEventListener('click', function() { $("#casterSkills > img").removeClass("caster-selected"); }); document.querySelector("#casterCastAll").addEventListener('click', async function() { // Check to see if point potion boosters are selected if (document.querySelector("#boosterPotions")){ if (document.querySelector("#boosterPotions").classList.contains("caster-selected")) { await superfetch('userbuff.php?castboost=1',true); await superfetch('userbuff.php?castboost=1',true); await superfetch('userbuff.php?castboost=1',true); await superfetch('userbuff.php?castboost=1',true); await superfetch('userbuff.php?castboost=1',true); }; }; // Select all img elements inside the casterDiv with the class "caster-selected" const selectedImages = document.querySelector("#casterDiv").querySelectorAll("img.caster-selected"); const imgIds = Array.from(selectedImages).map(img => img.id); await mmplus(`Cast|rganame|${server}|${charId}|${imgIds.join(',')}`); await casterMenuClose(); }); // Save preset button document.querySelector("#saveAsPreset").addEventListener('click', async function(){ const selectedImages = document.querySelector("#casterDiv").querySelectorAll("img.caster-selected"); const imagesArray = Array.from(selectedImages, node => node.currentSrc).map(url => url.replace("https://torax.outwar.com/", "")); const presetName = prompt('Please name this preset'); if (presetName == ''){ alert(`You did not enter a name for the preset. It wasn't saved`); } else if (presetName.length > 20){ alert(`Error: Maximum length allowed is 20 characters`); } else { for (let i = 0; i < 10; i++) { const value = GM_getValue(`casterPreset(${i})`); if (!value){ GM_setValue(`casterPreset(${(i)})`, presetName + "|" + imagesArray.toString()); document.querySelector("#casterPresetsDrop").innerHTML += `<option id="casterPreset(${i})" value="casterPreset(${i})" data-images="${imagesArray}">${presetName}</option>` break; }; if (i == 9){ alert(`You aleady have the maximum of 10 presets. Please delete one before adding more.`); }; }; }; }); // Load saved presets for (let i = 0; i < 10; i++) { const value = GM_getValue(`casterPreset(${i})`); if (value){ // Parse group data from values const presetName = value.split('|').slice(0, 1); const presetArray = value.split('|').slice(1); document.querySelector("#casterPresetsDrop").innerHTML += `<option id="casterPreset(${i})" value="casterPreset(${i})" data-images="${presetArray}">${presetName}</option>` }; }; // Delete preset document.querySelector("#deletePreset").addEventListener('click', async function(){ const targetPreset = document.querySelector("#casterPresetsDrop").value; document.querySelector(`[id="${targetPreset}"]`).remove(); GM_deleteValue(targetPreset); }); // Select preset document.querySelector("#casterPresetsDrop").addEventListener('change', async function() { // Extract the data-images attribute from the selected option const dataImages = this.options[this.selectedIndex].outerHTML.match(/data-images="([^"]*)"/i); const presetArray = dataImages[1].split(','); // Split the captured group into an array const casterDiv = document.getElementById("casterDiv"); // Remove the 'caster-selected' class from all images in casterDiv casterDiv.querySelectorAll("img.caster-selected").forEach(img => { img.classList.remove("caster-selected"); }); // Loop through each image source in the presetArray presetArray.forEach(imageSrc => { casterDiv.querySelectorAll("img").forEach(img => { // Check if the img's src includes the desired substring if (img.src.includes(imageSrc.trim())) { img.classList.add("caster-selected"); // Add the class if it matches }; }); }); }); // Slider animation GM_addStyle (`#casterDiv {animation: moveAnimation 0.5s ease forwards;position:fixed;}`) GM_addStyle (`@keyframes moveAnimation {0% {width:700px;height:0px;position:fixed;top:0px;} 100% {width:700px;height:535px;position:fixed;top:45px;}}`) }; // Search menu async function searchMenu(rgaName,charId,serverNo) { casterMenuClose(); appsMenuClose(); travelMenuClose(); // Toggle const menu = document.querySelector("#btnSearch"); if (!menu.classList.contains("menu-created")) { await searchMenuBuild(rgaName,charId,serverNo); menu.classList.add("menu-created"); } if (menu.classList.contains("menu-open")) { await searchMenuClose(); } else { await searchMenuOpen(rgaName,charId,serverNo); menu.classList.add("menu-open"); menu.classList.remove("menu-close"); }; }; // Opens the search menu async function searchMenuOpen(rgaName,charId,serverNo) { // Rotate the icon image document.querySelector("#btnSearch > img").classList.toggle('rotate180'); GM_addStyle (`#btnSearch > img {transition: transform 0.5s ease;}`); GM_addStyle (`.rotate180 {transform: rotate(180deg);}`); // Slider animation GM_addStyle (`#searchDiv {animation: moveAnimation 0.5s ease forwards;position:fixed;}`) GM_addStyle (`@keyframes moveAnimation {0% {width:270px;height:0px;position:fixed;top:0px;} 100% {width:270px;height:275px;position:fixed;top:45px;}}`) // Focus on character search document.querySelector("#t-text").select(); }; async function searchMenuBuild(rgaName,charId,serverNo){ // Build search menu const searchHtml = ` <h4>Search</h4> <form method="post" action="playersearch.php" target="_parent" style="display:inline-block;"> <input style="width:230px;" id="t-text" name="search" type="text" placeholder="PLAYER" class="form-control-new mb-2" autocomplete="off"> </form> <br> <form method="post" action="crewsearch.php" target="_parent" style="display:inline-block"> <input style="width:230px;" id="t-text" name="search" type="text" placeholder="CREW" class="form-control-new mb-2" autocomplete="off"> </form> <br> <input style="width:230px;" id="treasurySearch" type="text" placeholder="TREASURY" class="form-control-new mb-2" autocomplete="off"> <br> <input style="width:230px;" id="rgaSearch" type="text" placeholder="ITEM ON RGA" class="form-control-new mb-2" autocomplete="off"> <br> <div id="rgaSearchDiv"></div> ` // Create div var image = document.querySelector("#btnSearch").getBoundingClientRect(); var newDiv = document.createElement('div'); newDiv.id = 'searchDiv'; newDiv.classList.toggle('widget'); newDiv.innerHTML = searchHtml; newDiv.style.position = 'absolute'; newDiv.style.top = image.top + window.scrollY + 'px'; newDiv.style.left = image.left + window.scrollX-220 + 'px'; document.body.appendChild(newDiv); // Treasury search document.querySelector("#treasurySearch").addEventListener("keyup", function(event) { // Check if the key pressed is Enter if (event.keyCode === 13) { const treasuryLookupItem = document.querySelector("#treasurySearch").value.replace(/ /g,'+') window.location.href = `treasury?search_for=${treasuryLookupItem}` }; }); // RGA item search document.querySelector("#rgaSearch").addEventListener("keyup", async function(event) { // Check if the key pressed is Enter if (event.keyCode === 13) { // Disable page elements while search runs document.body.style.pointerEvents = 'none'; document.querySelector("#container").setAttribute('style','opacity:0.25; transition: opacity 0.5s ease;'); document.querySelector("body > center > div.sub-header-container").setAttribute('style','opacity:0.25; transition: opacity 0.5s ease;'); // Get starting char ID const startingCharId = document.body.innerHTML.match(/<option value="([0-9]+)" selected/i)[1] // Animate GM_addStyle (`#searchDiv {animation: growSearch 0.5s ease forwards;position:fixed;}`) GM_addStyle (`@keyframes growSearch {0% {width:270px;height:275px;position:fixed;top:45px;} 100% {width:270px;height:500px;position:fixed;top:45px;}}`) // Searching for item const itemName = document.querySelector("#rgaSearch").value.toUpperCase() document.querySelector("#rgaSearchDiv").innerHTML = ` <div id="rgaSearchResults" style="max-height:280px;overflow:auto;"> <img src="https://studiomoxxi.com/moxximod/loading-gif.gif" style="height:75px;width:75px;padding:10px"> </div> `; // Find the items const charDropdown = document.body.innerHTML.replace(/[\n\r]/g,'').match(/<optgroup label="My Characters">.*?<option value="0">--Change Server--<\/option>/i) const charIdArray = charDropdown.toString().match(/value="[0-9]+"/g).map(match => match.match(/[0-9]+/)[0]).slice(0, -1); let rows = [] const lookForItem = async (id) => { let found = 0; // Check backpacks const matchBp = new RegExp(`data-itemidqty="[0-9]+" data-name="${itemName}"`, 'i'); const types = ['regular', 'quest', 'orb', 'potion', 'key']; for (const type of types) { const look = await superfetch(`ajax/backpackcontents.php?tab=${type}&suid=${id}`); found += look.match(matchBp) ? parseInt(look.match(matchBp).toString().match(/data-itemidqty="([0-9]+)"/i)[1]) : 0 }; // Check equipment const matchEq = new RegExp(`alt="${itemName}"`, 'i'); const lookEq = await superfetch(`equipment?suid=${id}`); found += lookEq.match(matchEq) ? 1 : 0 // If 1 or more is found if (found > 0){ const profile = await superfetch(`profile?suid=${id}`); const name = `<a href='home.php?suid=${id}'>${profile.match(/<font size="3">(.*?)<\/font>/i)[1]}</a>`; rows.push(`<tr><td>${name}</td><td>${found}</td></tr>`); }; }; await Promise.all(charIdArray.map(lookForItem)); // Go back to starting char in the backed await superfetch(`ajax_changeroomb.php?suid=${startingCharId}`); // Build the table; document.querySelector("#rgaSearchResults").innerHTML = ` <table class="table table-striped sortable" style="text-align:left;border-radius:10px;box-shadow: 0 0 3px rgba(0, 0, 0, 0.5);"> <thead><tr><th>Char</th><th>Qnt</th></tr></thead> <tbody> ${rows.join('')} </tbody> </table> ` // Make table sortable await sortableTables(); // Re-enable page elements after search finishes document.body.style.pointerEvents = 'auto'; document.querySelector("#container").setAttribute('style','opacity:1; transition: opacity 0.5s ease;'); document.querySelector("body > center > div.sub-header-container").setAttribute('style','opacity:1; transition: opacity 0.5s ease;'); }; }); }; // Closes the search menu async function searchMenuClose(){ const menu = document.querySelector("#btnSearch"); menu.classList.add("menu-close"); menu.classList.remove("menu-open"); document.querySelector("#btnSearch > img").classList.remove('rotate180'); GM_addStyle (`#searchDiv {animation: rewindSearch 0.5s ease forwards;}`) GM_addStyle (`@keyframes rewindSearch {0% {width:270px;height:275px;position:fixed;top:45px;} 100% {width:270px;height:0px;position:fixed;top:-1000px;}}`) // Reset the rga item search div to empty if (document.querySelector("#rgaSearchDiv")){ document.querySelector("#rgaSearchDiv").innerHTML = ''; } } // Closes the fast travel menu async function travelMenuClose(){ const menu = document.querySelector("#btnTravel"); menu.classList.add("menu-close"); menu.classList.remove("menu-open"); document.querySelector("#btnTravel > img").classList.remove('rotate180'); GM_addStyle (`#travelDiv {animation: rewindTravel 0.5s ease forwards;}`) GM_addStyle (`@keyframes rewindTravel {0% {width:370px;height:535px;position:fixed;top:45px;} 100% {width:370px;height:0px;position:fixed;top:-1000px;}}`) } // Closes the apps menu async function appsMenuClose(){ const menu = document.querySelector("#btnApps"); menu.classList.add("menu-close"); menu.classList.remove("menu-open"); document.querySelector("#btnApps > img").classList.remove('rotate180'); GM_addStyle (`#appsDiv {animation: rewindApps 0.5s ease forwards;}`) GM_addStyle (`@keyframes rewindApps {0% {width:535px;height:535px;position:fixed;top:45px;} 100% {width:535px;height:0px;position:fixed;top:-1000px;}}`) } // Closes the quick cast menu async function casterMenuClose(){ const menu = document.querySelector("#btnCaster"); menu.classList.add("menu-close"); menu.classList.remove("menu-open"); document.querySelector("#btnCaster > img").classList.remove('rotate180'); GM_addStyle (`#casterDiv {animation: rewindCaster 0.5s ease forwards;}`) GM_addStyle (`@keyframes rewindCaster {0% {width:700px;height:535px;position:fixed;top:45px;} 100% {width:700px;height:0px;position:fixed;top:-1000px;}}`) }; // Boss spawns page rebuild async function bossSpawns(){ // Styling const size = 280 GM_addStyle (` #bossSpawnDiv{width:1030px;text-align:left;} div.bossSpawnTile{display:inline-block;height:${size}px;overflow:hidden;box-shadow: 0 0 10px rgba(0, 0, 0, 1);margin:4px;text-align:center;border-radius:4px;} div.bossSpawnTile > img{width:${size}px;height:${size}px;} div.bossSpawnText{width:${size-30}px;position:relative;left:15px;top:-200px;background:#000000;padding:15px;border-radius:15px;font-size:14px;opacity:0.9;box-shadow: 0 0 3px rgba(0, 0, 0, 1);} `); // Get all bosses from boss page var bosses = document.querySelectorAll(".col-12.col-xl-6"); const bossesArray = []; bosses.forEach(function(div) { const i = div.innerHTML const img = i.match(/img src="([^"]*)"/i)[1] const name = i.match(/<b>(.*?)<\/b>/i)[1].replace(/(,.*| .*)/g, '') const spawnid = i.match(/boss_stats\.php\?spawnid=([0-9]+)/i)[1] const form = i.match(/formraid\.php\?target=[0-9]+/i) ? `<a href="${i.match(/formraid\.php\?target=[0-9]+/i)}">Form Raid</a>` : '' const health = i.match(/<p class="card-user_occupation">.*?[0-9]+%/i) ? `<p><h5>${i.match(/<p class="card-user_occupation">.*?([0-9]+)%/i)[1]}%</h5>` : '' bossesArray.push(` <div class="bossSpawnTile"> <img src="${img}"><br> <div class="bossSpawnText"> <h5><b>${name.toUpperCase()}</b></h5> <a href="boss_stats.php?spawnid=${spawnid}">Crew Table</a> ${health} ${form} </div> </div> `); }); document.querySelector("#content").innerHTML = ` ${bossesArray.join('')} `; }; async function bossStats(){ // Styling GM_addStyle(` img.bossCrewPic{width:30px;height:30px;} div.bossStatsContainer{position:relative;width: 50%;flex-grow: 8;margin-top: 130px;margin-bottom: 0;margin-left: 212px;max-width: 1300px;} @keyframes fadeIn {from {opacity: 0;} to {opacity: 1;}} #content{display:none !important;} #bossTableDiv{animation: bossTable 2s ease forwards;} @keyframes bossTable {0% {opacity:0;} 100% {opacity:1}}"); `) // Get boss name const boss = document.body.innerHTML.match(/<h1 class="w-100">(.*?)<\/h1>/i)[1]; const specs = await info(boss); // Build new div element to add to the bottom after hiding native data so that it's retained to get data const newDiv = document.createElement("div"); newDiv.classList.add('bossStatsContainer') newDiv.innerHTML = ` <div class="widget mb-3"> <h1>${boss} <a onmouseover="popup(event,'${specs[2]}')" onmouseout="kill()"><img src="https://studiomoxxi.com/moxximod/loot.png" height="20px" style="margin-bottom:10px"></a> </h1> <span id="hpremaining"></span> </div> <img src="https://studiomoxxi.com/moxximod/loading-gif.gif" width="80px" height="80px" id="loading"> <div id="bossTableContainer"> </div> ` document.querySelector("#container").appendChild(newDiv); // Get table rows for native data const rows = document.querySelectorAll("#content-header-row > div > table > tbody > tr"); let totItems = 0; let obj = [] rows.forEach(function(i) { const crew = (i.innerHTML.match(/<font color="#CC0000".*?>(.*?)<\/font>/i) || ['error','error'])[1]; const id = i.innerHTML.match(/crew_profile\.php\?id=([0-9]+)/i)[1]; const dmg = i.innerHTML.replace(/,|\(.*?\)| /g, '').match(/<td>([0-9]+)<\/td>/i)[1]; const items = i.innerHTML.replace(/'/g,'').match(/<td onmouseover="popup\(event,([^']*),808080\)" onmouseout="kill\(\)">([0-9]+)<\/td>/i); // Create array of data from native table obj.push({crew: crew, id: id, dmg: dmg, loot: items[1], count: items[2]}) // Count total items found totItems += parseInt(items[2]); }); if (totItems > 0){ await bossStatus_dead(obj); } else { await bossStatus_alive(boss,obj,specs); }; }; async function bossStatus_alive(boss,obj,specs){ GM_addStyle(` #almostDeadTimer {color: #ff0000;font-size:20px;} `) const data = []; // Get total damage const total = obj.reduce((acc, obj) => acc + parseInt(obj.dmg), 0); const hp = specs[0]; const totloot = specs[1]; document.querySelector("#hpremaining").innerHTML = `<h5>Health Remaining: ${(hp-total).toLocaleString()} (${((hp-total)/hp*100).toFixed(3)}%)</h5>` let active = 0; let dmgpmin = 0; const getData = async (c) => { // Crew name const crew = c.crew; // Crew id const id = c.id; // Crew total damage const dmg = parseInt(c.dmg); // Percentage const perc = (dmg/total*100).toFixed(3); // Fetch raid results const fetch = await superfetch(`crew_raidresults.php?crewid=${id}`); const regex = new RegExp(`<td align="left" valign="top">${boss}<\/td>`) const last = fetch.match(/<tr>[\s\S]*?<\/tr>/g).filter(tr => tr.match(regex)); // Calculate time since last raid const raidtime = last.length > 0 ? last[0].match(/[0-9]+-[0-9]+-[0-9]+ [0-9]+:[0-9]+[A-Za-z]m/i)[0].replaceAll("-","/").replace("am",":00 AM").replace("pm",":00 PM").replace(/ 0/i," ") : '01/01/01 01:01:01 AM'; var d = new Date(); var utc = d.getTime() + (d.getTimezoneOffset() * 60000); var owtime = new Date(utc + (3600000 * parseFloat('-5.0'))); const sinceLast = (owtime - new Date(raidtime))/3600000 // Get data from last raid const raidid = last.length > 0 ? last[0].match(/raidid=([0-9]+)/i)[1] : '0'; const raidattack = await superfetch(`raidattack.php?raidid=${raidid}`); const parse = new DOMParser().parseFromString(raidattack, 'text/html'); // Get damage from last raid const raiddmg = raidattack.replace(/,/g,'').match(/<i>Total Attacker Damage: [0-9]+<\/i>/i) ? parseInt(raidattack.replace(/,/g,'').match(/<i>Total Attacker Damage: ([0-9]+)<\/i>/i)[1]).toLocaleString() : '--'; let status; if (sinceLast <= 0.033){ status = '<b><font color="#00ff00">ACTIVE</font></b>'; dmgpmin += parseInt(raiddmg.replace(/,/g,'')); active += 1; } else if (sinceLast < 1){ status = '<b><font color="#00ff00">ACTIVE</font></b>'; active += 1; } else { status = `<a style="opacity:0.5">INACTIVE</a>`; }; // Char from last raid const char = (parse.querySelector("#message_0 > div > div > span > b") || {textContent: "na"}).textContent.replace(/ .*/i,"") const profileData = await superfetchProfile(`profile?transnick=${char}`); const markdown = profileData.skills.list.includes('Markdown') ? profileData.skills.images.toString().match(/<b>Level [0-9]+ Markdown<\/b><br \/>You become more focused and alert for battle\. Decreases cost by [0-9]+% to form\/join raids\.<br \/>(.*?) left<br>/i)[1] : "--" // Crew profile const crewPro = await superfetch(`crew_profile?id=${id}`); const pic = crewPro.match(/https:\/\/upload\.outwar\.com\/crewuploaded\/[A-Za-z][0-9]+\.[A-Za-z]+/i) || "images/logodefault.gif"; // Build table data row data.push({ pic: `<img src="${pic}" class="bossCrewPic">`, name: `<a href="crew_profile?id=${id}">${crew}</a>`, dmg: dmg, perc: `${perc}%`, items: (parseInt(dmg)/hp*totloot).toFixed(1), projected: (perc/100*totloot).toFixed(1), status: status, raid: raiddmg, md: markdown }); }; await Promise.all(obj.map(getData)); // Sort table rows data.sort((a, b) => b.dmg - a.dmg); // Build HTML rows const rows = data.map(({ pic, name, dmg, perc, items, projected, status, raid, md }) => ` <tr> <td>${pic}</td> <td>${name}</td> <td>${dmg.toLocaleString()}</td> <td>${perc}</td> <td>${items}</td> <td>${projected}</td> <td>${status}</td> <td>${raid}</td> <td>${md}</td> </tr> `); // Remove loading spinner document.querySelector("#loading").remove(); // Build crew table HTML document.querySelector("#bossTableContainer").innerHTML = ` <div id="bossTableDiv" class="widget"> <div class="list-group-item"> <div id="almostDeadTimer"></div><i>There are ${active} crews actively raiding doing ${dmgpmin.toLocaleString()} damage per minute</i> </div> <table class="table table-striped"> <thead><tr><th></th><th>Crew</th><th>Damage</th><th>Percent</th><th>Items</th><th>Projected</th><th>Status</th><th>Last Raid</th><th>Markdown</th></tr></thead> <tbody> ${rows.join('')} </tbody> </table> </div> ` const timeUntilDeath = (hp-total)/dmgpmin; if (timeUntilDeath < 200){ document.querySelector("#almostDeadTimer").innerHTML = `${boss} will die in about ${Math.ceil(timeUntilDeath)} minutes` } }; async function bossStatus_dead(obj){ const highlight = { value: ['Blazing Serpent','Ghostly Stalker','Exalted Perfection','Transcended Extract'], rare: ['Catalyst','Augment of Madness','Tier 2 Booster Upgrade','Reborn Knight','8-Bit Banana','Blossom'], epic: ['Augment of Simulation','Descendant','Ancestral','Boon of Vision'] }; const data = []; // Get data const getData = async (c) => { // Crew name const crew = c.crew; // Crew id const id = c.id; // Crew total damage const dmg = parseInt(c.dmg); // Number of items found const count = c.count; // All drops const loot = c.loot.split('<br>'); // Check for rare drops const lootStyled = loot.map(value => highlight.value.some(item => value.includes(item)) ? `<font color="#FFDE5B">${value}</font>` : highlight.rare.some(item => value.includes(item)) ? `<font color="#FF8000">${value}</font>` : highlight.epic.some(item => value.includes(item)) ? `<font color="#FF00FF">${value}</font>` : value ); // Crew profile const crewPro = await superfetch(`crew_profile?id=${id}`); const pic = crewPro.match(/https:\/\/upload\.outwar\.com\/crewuploaded\/[A-Za-z][0-9]+\.[A-Za-z]+/i) || "images/logodefault.gif"; data.push({id:id,crew:crew,pic:pic,id:id,dmg:dmg,count:count,loot:lootStyled.slice(0, -1).join(', ')}); }; await Promise.all(obj.map(getData)); // Sort table rows data.sort((a, b) => b.dmg - a.dmg); // Build HTML rows const rows = data.map(({ id, crew, pic, dmg, count, loot}) => ` <tr> <td><img src="${pic}" class="bossCrewPic"></td> <td><a href="crew_profile?id=${id}">${crew}</a></td> <td>${dmg.toLocaleString()}</td> <td>${count}</td> <td>${loot}</td> </tr> `); // Remove loading spinner document.querySelector("#loading").remove(); // Build crew table HTML document.querySelector("#bossTableContainer").innerHTML = ` <div id="bossTableDiv" class="widget"> <table class="table table-striped"> <thead><tr><th></th><th>Crew</th><th>Damage</th><th>Items</th><th>Loot</th></tr></thead> <tbody> ${rows.join('')} </tbody> </table> </div> ` }; // Item transfer rebuild async function itemTransfer(){ // Styling GM_addStyle(` #items > img, #itemsSelectedDiv > img {position:revert;height:40px;width:40px;margin:5px;border-radius:5px;transform:translate(0%,0%);background-color:#000000;} .item-desel {border:2px #475254 SOLID !important;} .item-sel {border:3px #00CC00 SOLID !important;padding:2px;animation: item-selected 1s infinite;} #itemsSelectedDiv > img{cursor: pointer;} @keyframes item-selected{0% {filter: saturate(100%);} 50% {filter: saturate(250%); } 100% {filter: saturate(100%);}} `) // Character dropdown menu const charDropdown = document.querySelector("#divHeader > select") charDropdown.classList.add('form-control-new') charDropdown.setAttribute('style','font-size:15px;padding:0.75rem;border-radius:6px;margin-bottom:0.5rem;width:100%;'); // Get all items and set class to "item" let allItems = (document.querySelector("#divItemtransfer").innerHTML.match(/<img alt="[^"]*" style="[^"]*" src="[^"]*" onmouseover="itempopup\(event,[0-9]+, [0-9]+, '', [0-9]+\)" onmouseout="kill\(\)">/g) ?? []).map(i => (i.replace('src','class="item item-desel" src'))).join(''); // Build HTML document.querySelector("#content").innerHTML = ` <div class="row justify-content-center" id="content-header-row"> <div class="bio col-lg-8 col-md-8 col-sm-12 col-12 layout-spacing layout-spacing" id="availableItemsToTransfer"> <div style="display:inline-block;text-align:left; width: 100%;" id="items"> <h4>Item Transfer</h4><hr style="margin-top:10px;margin-bottom:10px;"> ${allItems} </div></div> <div class="col-lg-4 col-md-4 col-sm-12 col-12 layout-spacing layout-spacing" style="text-align:left"> <div class="widget text-left mb-3" id="itemsSelectedOuterDiv"> <h6>Items Selected: <span id="selectedCount">0</span></h6> <div id="itemsSelectedDiv"></div> </div> <div class="widget text-left mb-3"> <span id="sendItemsText"><h4>Send Items</h4></span> ${charDropdown.outerHTML}<br> <button id="sendItems" class="btn-mm">SEND ITEMS</button><button id="sendAndGo" class="btn-mm">SEND AND GO</button> <hr> <a href="javascript:void(0);" id="setDefaultChar">Set default transfer character</a><p><span id="togglePb"></span><p> <i><b>SEND AND GO</b> will transfer the selected items and then change characters to the character receiving the items</i> </div></div></div> ` // Toggle playerbound items on and off if (window.location.href.match('Playerbound=1')){ document.querySelector("#togglePb").innerHTML = `<a href="itemtransfer.php?includePlayerbound=0">Hide playerbound items</a>` } else { document.querySelector("#togglePb").innerHTML = `<a href="itemtransfer.php?includePlayerbound=1">Show playerbound items</a>` }; // Select default char if stored if (GM_getValue("transferDefault")){ const charId = GM_getValue("transferDefault") document.querySelector('select[name="self"]').value = charId; }; // Item handling const items = document.querySelectorAll(".item") const itemHandling = async (item) => { // On click item.addEventListener('click', async function(){ item.classList.toggle('item-sel') item.classList.toggle('item-desel') await updateItemsSelected(); }); // On double click item.addEventListener('dblclick', async function(){ const itemName = item.outerHTML.match(/alt="([^"]*)"/i)[1] const all = document.querySelectorAll(`img[alt="${itemName}"]`); const selectAll = async (item) => { item.classList.toggle('item-sel') item.classList.toggle('item-desel') } await Promise.all(Array.from(all).map(selectAll)); await updateItemsSelected(); }); }; await Promise.all(Array.from(items).map(itemHandling)); // Update the selected items div async function updateItemsSelected(){ document.querySelector("#itemsSelectedDiv").innerHTML = '' const selectedItems = document.querySelectorAll(".item-sel"); const selectedArray = [] for (let i = 0; i < selectedItems.length; i++) { selectedArray.push(selectedItems[i].outerHTML.replace('item-sel','item-desel')) }; document.querySelector("#itemsSelectedDiv").innerHTML = selectedArray.join(''); document.querySelector("#selectedCount").innerHTML = selectedArray.length; }; // Send button document.querySelector("#sendItems").addEventListener('click', async function(){ await loadingOverlay() const itemIds = document.querySelector("#itemsSelectedDiv").innerHTML.match(/event,[0-9]+/g).map(i => `&checkbox%5B%5D=${i.match(/[0-9]+/)[0]}`).join(''); const charId = document.querySelector('select[name="self"]').value; await superpost('itemtransfer',`self=${charId}&submit=Send+items%21${itemIds}`); // Remove transferred items document.querySelector("#itemsSelectedDiv").innerHTML = ''; const selectedItems = document.querySelectorAll(".item-sel"); document.querySelector("#selectedCount").innerHTML = '0' for (let i = 0; i < selectedItems.length; i++) { selectedItems[i].remove(); }; await loadingOff(); }); // Send and go button document.querySelector("#sendAndGo").addEventListener('click', async function(){ await loadingOverlay() const itemIds = document.querySelector("#itemsSelectedDiv").innerHTML.match(/event,[0-9]+/g).map(i => `&checkbox%5B%5D=${i.match(/[0-9]+/)[0]}`).join(''); const charId = document.querySelector('select[name="self"]').value; await superpost('itemtransfer',`self=${charId}&submit=Send+items%21${itemIds}`); // Remove transferred items document.querySelector("#itemsSelectedDiv").innerHTML = ''; const selectedItems = document.querySelectorAll(".item-sel"); document.querySelector("#selectedCount").innerHTML = '0' for (let i = 0; i < selectedItems.length; i++) { selectedItems[i].remove(); }; window.location.href = `world?suid=${charId}` }); // Set default char button document.querySelector("#setDefaultChar").addEventListener('click', function(){ const charId = document.querySelector('select[name="self"]').value; GM_setValue("transferDefault",charId); }); // Badge of Absolution item drop down menu modification link to trigger necessary function if (window.location.href.match('type=selectbadge')){ await selectBadge(); }; }; // Action for the Badge of Absolution item drop down menu modification that will auto-select the badge on the transfer item page async function selectBadge(){ GM_addStyle(`#itemsSelectedOuterDiv{display:none;}`) document.querySelector(`img[src*="images/items/badge26a.gif"]`).click(); document.querySelector("#sendItemsText").innerHTML = `<h5>Transfer Badge of Absolution</h5><br>` document.querySelector("#setDefaultChar").remove(); document.querySelector("#availableItemsToTransfer").remove(); document.querySelector("#togglePb").innerHTML = `<i>Completing badge transfer will load the selected account in your browser</i>`; document.querySelector("#sendItems").addEventListener('click', async function(){ const selectedCharId = document.querySelector("#content-header-row > div > div:nth-child(2) > select").value //await superpost(`ajax/backpack_action.php?suid=${selectedCharId}`,`action=equip&itemids%5B%5D=505739120`) window.location.href = `world?suid=${selectedCharId}` }); }; // Crew vault rebuild async function crewVault(){ // Styling GM_addStyle(` #cvPopUpDiv{width:100%;overflow-wrap: break-word;;} .crew_itembox_item{position:revert;height:40px;width:40px;max-width:40px;max-height:40px;margin:5px;border-radius:5px;transform:translate(0%,0%);border:2px #475254 SOLID !important;background-color:#000000;} .crew_itembox_item:hover{animation: pulse 1s infinite;cursor: pointer;} .item-selected{border:3px #00CC00 SOLID !important;padding:2px;animation: item-selected 1s infinite;} #itemsSelectedDiv > img{cursor: pointer;} @keyframes item-selected{0% {filter: saturate(100%);} 50% {filter: saturate(250%); } 100% {filter: saturate(100%);}} .btn-mm{margin-left:0px;} img.cvSelectAllImg{height:40px;width:40px;margin:2px;border-radius:5px;transform:translate(0%,0%);border: 2px #475254 SOLID !important;background-color: #000000;} img.cvSelectAllImg:hover{animation: pulse 1s infinite;cursor: pointer;} `) // Check for crew vault access const manager = document.body.innerHTML.match(/<h5>Award Item<\/h5>/i) ? true : false; // Local variables let cvItems; const points = document.body.innerHTML.replace(/,/g,'').match(/event'([0-9]+\.[0-9]+) Crew Points/i)[1] const capacity = document.body.innerHTML.match(/Currently Storing <b>([0-9]+) \/ ([0-9]+)<\/b>/i) // MANAGER: Build HTML if (manager){ let nonce = await nonceGet(); const awardTo = document.querySelector('select[name="awardto"]'); const rbSel = document.querySelector('select[name="rbsel"]'); cvItems = document.body.innerHTML .replace(/[\n\r]/g,'') .match(/<div class="crew_itembox">.*?<\/div>/g) .filter(i => !i.match('treasuryimg')) .map(i => i.match(/<div class="crew_itembox">(.*?)<\/div>/)[1]); document.querySelector("#content").innerHTML = ` <div class="row justify-content-center" id="content-header-row"> <div class="bio col-lg-8 col-md-8 col-sm-12 col-12 layout-spacing layout-spacing"> <div style="display:inline-block;text-align:left; max-width: 100%;" id="cvitems"> <h4>Crew Vault</h4><hr style="margin-top:10px;margin-bottom:10px;"> <div id="cvItems"> ${cvItems.join('')} </div><br><br>  <i>*Items listed on crew treasury will not appear in MoxxiMod crew vault</i> </div></div> <div class="col-lg-4 col-md-4 col-sm-12 col-12 layout-spacing layout-spacing" style="text-align:left"> <div class="button-container mb-3" style="display:inline-block;"><div class="btn-group show" role="group"> <button type="button" class="btn-mm dropdown-toggle" data-toggle="dropdown">STORAGE MENU <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-down"><polyline points="6 9 12 15 18 9"></polyline></svg></button> <div class="dropdown-menu" style="will-change: transform; position: absolute; transform: translate3d(0px, 38px, 0px); top: 0px; left: 0px;"> <a class="dropdown-item" href="crew_pointbank">Point Bank</a> <a class="dropdown-item" href="crew_treasury">Crew Treasury</a> <a class="dropdown-item" href="crew_stones">Upgrade Stones</a> <a class="dropdown-item" href="crew_actionlog.php?l=Award%20Item">Award Log</a> <a class="dropdown-item" href="crew_actionlog.php?l=Delete%20Item">Deleted Log</a> </div></div></div> <div class="button-container mb-3" style="display:inline-block;"><div class="btn-group show" role="group"> <button type="button" class="btn-mm dropdown-toggle" data-toggle="dropdown">QUICK SELECT <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-down"><polyline points="6 9 12 15 18 9"></polyline></svg></button> <div class="dropdown-menu" style="will-change: transform; position: absolute; transform: translate3d(0px, 38px, 0px); top: 0px; left: 0px;"> <img id="saEthl" src="images/items/arheart.jpg" class="cvSelectAllImg" onmouseover="statspopup(event,'Ethereal Quest Items')" onmouseout="kill()"> <img id="saLffc" src="images/edeath.gif" class="cvSelectAllImg" onmouseover="statspopup(event,'Life Forces')" onmouseout="kill()"> <img id="saSlgm" src="images/soulgem1.jpg" class="cvSelectAllImg" onmouseover="statspopup(event,'Soul Gems')" onmouseout="kill()"><br> <img id="saWilk" src="images/items/arqitem1.jpg" class="cvSelectAllImg" onmouseover="statspopup(event,'Wilker Quest Items')" onmouseout="kill()"> <img id="saWleq" src="images/items/blackkingchest.jpg" class="cvSelectAllImg" onmouseover="statspopup(event,'Wonderland Equipment')" onmouseout="kill()"> <img id="saFndr" src="images/item_arcane.gif" class="cvSelectAllImg" onmouseover="statspopup(event,'Foundry Augments')" onmouseout="kill()"><br> <img id="saTrnk" src="images/items/whitetrinket.jpg" class="cvSelectAllImg" onmouseover="statspopup(event,'Trinkets')" onmouseout="kill()"> <img id="saSprl" src="images/items/UberChallengeCore.gif" class="cvSelectAllImg" onmouseover="statspopup(event,'Spiral World Set Items')" onmouseout="kill()"> <img id="saNbld" src="images/items/5002.gif" class="cvSelectAllImg" onmouseover="statspopup(event,'Noble Lord Set Items')" onmouseout="kill()"><br> <img id="saPrfc" src="images/items/PerfCore.gif" class="cvSelectAllImg" onmouseover="statspopup(event,'Perfectionist Set Items')" onmouseout="kill()"> <img id="saExlt" src="images/items/exaltedcore.gif" class="cvSelectAllImg" onmouseover="statspopup(event,'Exalted Perfection Set Items')" onmouseout="kill()"> <img id="saGhst" src="images/items/ghostlycore.gif" class="cvSelectAllImg" onmouseover="statspopup(event,'Ghostly Stalker Set Items')" onmouseout="kill()"><br> <img id="saDrgn" src="images/items/dset_weapon.gif" class="cvSelectAllImg" onmouseover="statspopup(event,'Dragon Set Items')" onmouseout="kill()"> <img id="saChnc" src="images/items/FGRWep.gif" class="cvSelectAllImg" onmouseover="statspopup(event,'Chancellor Set Items')" onmouseout="kill()"> <img id="saBlck" src="images/items/BHR-Weapon.gif" class="cvSelectAllImg" onmouseover="statspopup(event,'Blackhand Set Items')" onmouseout="kill()"><br> <img id="saAmlt" src="images/items/AOAChest.jpg" class="cvSelectAllImg" onmouseover="statspopup(event,'Amulet Chests')" onmouseout="kill()"> <img id="saSmps" src="images/items/itema21.jpg" class="cvSelectAllImg" onmouseover="statspopup(event,'Guardian Stamps')" onmouseout="kill()"> <img id="saTlsm" src="images/items/itemz71.jpg" class="cvSelectAllImg" onmouseover="statspopup(event,'Boss Talismen')" onmouseout="kill()"><br> <img id="saVoid" src="images/items/voidmakerwep.gif" class="cvSelectAllImg" onmouseover="statspopup(event,'Voidmaker Set Item')" onmouseout="kill()"> </div></div></div> <div class="row mb-3"> <div class="col-6 pr-1"> <ul class="list-group text-left"> <li class="list-group-item"><b>Crew Points:</b> ${points}</li> <li class="list-group-item"><b>Sort:</b> <a href="crew_vault?order=1">Alpha</a> / <a href="crew_vault?order=2">Newest</a></li> </ul> </div><div class="col-6 pl-1"> <ul class="list-group text-left"> <li class="list-group-item"><b>Items:</b> ${capacity[1]}</li> <li class="list-group-item"><b>Capacity:</b> ${capacity[2]}</li> </ul> </div></div> <input id="cvMsg" type="text" class="form-control mb-2" style="color:#FF0000 !important;"> <div class="widget text-left mb-3"> <h6>Items Selected: <span id="selectedCount">0</span> <span id="amuletsSelected"></span></h6> <div id="itemsSelectedDiv"></div> <div id="cvPopUpDiv"></div> </div> <div class="widget text-left mb-3"> <h4>Award by Name</h4> <input id="awardByNameText" type="text" class="form-control mb-2"> <button id="awardByName" class="btn-mm">AWARD</button><hr> <h4>Award Items</h4> <div class="mb-2" id="awardToDiv">${awardTo.outerHTML}</div> <button id="awardItems" class="btn-mm">AWARD</button><hr> <h4>Raidbound Items</h4> <div class="mb-2" id="rbSelDiv">${rbSel.outerHTML}</div> <button id="selectItems" class="btn-mm">SELECT</button><hr> <h4>Delete Items</h4> <button id="deleteItems" class="btn-mm">DELETE</button> </div></div></div> ` // Item selection mutation observer and function await cvItemSelectionObserver(); async function cvItemSelectionObserver(){ var targetNodes = document.querySelectorAll('.crew_itembox_item'); var observer = new MutationObserver(function(mutationsList) { mutationsList.forEach(async mutation => { if (mutation.type === 'attributes' && mutation.attributeName === 'style') { mutation.target.classList.toggle('item-selected'); // Function to run on all items everytime a change is made const selectedItems = document.querySelectorAll('.item-selected'); const selectedArray = [] for (let i = 0; i < selectedItems.length; i++) { selectedArray.push(selectedItems[i].outerHTML.replace('item-selected','').replace(/ondblclick="[^"]*"/i,'')) }; // Set the value of the selected items div document.querySelector("#itemsSelectedDiv").innerHTML = selectedArray.join('') // Count the total number of items selected document.querySelector("#selectedCount").innerHTML = selectedItems.length // Check for amulets if (document.querySelector("#itemsSelectedDiv").innerHTML.match('Amulet Chest')){ const chestIDsArray = document.querySelector("#itemsSelectedDiv").innerHTML.match(/Amulet Chest \([0-9]+\)/g) let chestIDsValues = chestIDsArray.map(element => parseInt(element.match(/\d+/)[0])); let chestTotal = chestIDsValues.reduce((accumulator, currentValue) => accumulator + currentValue, 0); document.querySelector("#amuletsSelected").innerHTML = `(${chestTotal} Amulets)` } else { document.querySelector("#amuletsSelected").innerHTML = '' }; // Items Selected: Chaos Teleporters if (document.querySelector("#itemsSelectedDiv").innerHTML.match('Arena Teleporter')){ document.querySelector("#cvPopUpDiv").innerHTML = `<img src="https://studiomoxxi.com/moxximod/loading-gif.gif" style="height:30px;width:30px;margin:10px;">` const id = document.querySelector("#itemsSelectedDiv > img").outerHTML.match(/event,'([0-9]+)'/i)[1] const item = await superfetch(`item_rollover.php?id=${id}`); const chars = item.match(/ [A-Za-z0-9]+/g).map(i => (i.match(/ ([A-Za-z0-9]+)/)[1])); const array = [] const checkChars = async (char) => { const profileData = await superfetchProfile(`profile?transnick=${char}`); const power = profileData.power; const ele = profileData.elemental; const total = power + ele array.push([char,total]) }; await Promise.all(chars.map(checkChars)); const highestValueElement = array.reduce((acc, curr) => curr[1] > acc[1] ? curr : acc); const bestChar = highestValueElement[0]; document.querySelector("#cvPopUpDiv").innerHTML = `<br><b>Arena Teleporter selected</b><br>Strongest char available: <a href="javascript:void(0);" id="chaosTeleChar">${bestChar}</a>` document.querySelector("#chaosTeleChar").addEventListener('click', async function(){ document.querySelector("#awardByNameText").value = bestChar; }); // Items Selected: Artifacts } else if (document.querySelector("#itemsSelectedDiv").innerHTML.match('Artifact') && !document.querySelector("#itemsSelectedDiv").innerHTML.match('Lost') && document.querySelectorAll("#itemsSelectedDiv > img").length == 1){ document.querySelector("#cvPopUpDiv").innerHTML = `<img src="https://studiomoxxi.com/moxximod/loading-gif.gif" style="height:30px;width:30px;margin:10px;">` const id = document.querySelector("#itemsSelectedDiv > img").outerHTML.match(/event,'([0-9]+)'/i)[1] const item = await superfetchItem(id); const type = item.name.replace('Artifact of ',''); const array = [] const checkChars = async (char) => { const profileData = await superfetchProfile(`profile?transnick=${char}`); const charName = profileData.name; const completedSlayer = profileData.completedgodslayer.toString(); if (!completedSlayer.match(type)){ array.push(charName) } }; await Promise.all(item.raidboundArray.map(checkChars)); document.querySelector("#cvPopUpDiv").innerHTML = `<br><b>No ${type} god slayer found:</b><br>${array.join('<br>')}` // Items Selected: Other raidbound items } else if (document.querySelectorAll("#itemsSelectedDiv > img").length == 1){ const id = document.querySelector("#itemsSelectedDiv > img").outerHTML.match(/event,'([0-9]+)'/i)[1] const item = await superfetchItem(id); if (item.raidbound){ document.querySelector("#cvPopUpDiv").innerHTML = item.raidboundArray.join(',') }; } else { document.querySelector("#cvPopUpDiv").innerHTML = ""; } }; }); }); targetNodes.forEach(node => { observer.observe(node, { attributes: true }) }); } // Award By Name Button document.querySelector("#awardByName").addEventListener('click', async function(){ const name = document.querySelector("#awardByNameText").value; const regex = new RegExp(`<option value="([0-9]+)">${name.toLowerCase()}<\/option>`) const value = awardTo.outerHTML.toLowerCase().match(regex) if (value){ await loadingOverlay() await cvAward(value[1],name); document.querySelector("#awardByNameText").value = ''; } else { document.querySelector("#cvMsg").value = `${name} not found` }; }); // Award By Name ENTER Trigger document.getElementById("awardByNameText").addEventListener("keyup", function(event) { // Check if the key pressed is Enter key if (event.key === "Enter") { // Programmatically click the button document.getElementById("awardByName").click(); } }); // Award Button document.querySelector("#awardItems").addEventListener('click', async function(){ await loadingOverlay(); const dropdown = document.querySelector("#awardToDiv > select") const value = dropdown.value const regex = new RegExp(`<option value="${value}">(.*?)<\/option>`) const name = dropdown.innerHTML.match(regex)[1] await cvAward(value,name); }); // Raidbound Items Button document.querySelector("#selectItems").addEventListener('click', async function(){ await loadingOverlay(); const dropdown = document.querySelector("#rbSelDiv > select") const value = dropdown.value // Get selected char name const regex = new RegExp(`<option value="${value}">(.*?)<\/option>`) const name = dropdown.innerHTML.match(regex)[1] // Send post request const post = await superpost(`crew_vault`,`form-nonce=${nonce}&rbsel=${value}&selrb=Select+items`); if (post.replace(/[\n\r]/g,'').replace(/\s+/g,'').match(/id="v_item_border[0-9]+"src="[^"]*"style="border:2pxsolid#cc0000;"/i)){ document.querySelector("#awardByNameText").value = name; // Grab item IDs from post response const ids = post.replace(/[\n\r]/g,'').replace(/\s+/g,'').match(/id="v_item_border[0-9]+"src="[^"]*"style="border:2pxsolid#cc0000;"/g).map(i => i.match(/v_item_border([0-9]+)/i)[1]); // Click all items matching from response for (let i = 0; i < ids.length; i++) { const id = ids[i]; document.querySelector(`#v_item_border${id}`).click(); }; nonce = await nonceGet(); // If no raidbound items found, display a message } else { document.querySelector("#cvMsg").value = `No raidbound items for ${name}` nonce = await nonceGet(); }; await loadingOff(); }); // Delete Items Button document.querySelector("#deleteItems").addEventListener('click', async function(){ const selected = selectedArray = document.querySelector("#itemsSelectedDiv").innerHTML.match(/event,'[0-9]+'/g) if (selected){ const selectedArray = document.querySelector("#itemsSelectedDiv").innerHTML.match(/event,'[0-9]+'/g).map(i => `&v_selected%5B%5D=${i.match(/[0-9]+/i)}`) const confirm = window.confirm(`Are you sure you want to delete ${selectedArray.length} items?`); if (confirm){ const del = await superpost('crew_vault',`order=1${selectedArray.join('')}&form-nonce=${nonce}&awardto=0&rbsel=0&confirm_delete=on&delete=Delete+items`) if (del){ nonce = await nonceGet(); const ids = document.querySelector("#itemsSelectedDiv").innerHTML.match(/v_item_border[0-9]+/g).map(i => i.match(/[0-9]+/i)[0]) document.querySelector("#selectedCount").innerHTML = '0' document.querySelector("#itemsSelectedDiv").innerHTML = '' for (let i = 0; i < ids.length; i++) { const id = ids[i]; document.querySelector(`#v_item_border${id}`).remove() document.querySelector(`#v_selected_${id}`).remove() }; }; }; }; }); // Award function async function cvAward(value,name){ const selected = selectedArray = document.querySelector("#itemsSelectedDiv").innerHTML.match(/event,'[0-9]+'/g) if (selected){ const selectedArray = document.querySelector("#itemsSelectedDiv").innerHTML.match(/event,'[0-9]+'/g).map(i => `&v_selected%5B%5D=${i.match(/[0-9]+/i)}`) const award = await superpost('crew_vault',`order=1${selectedArray.join('')}&form-nonce=${nonce}&awardto=${value}&award=Award+Items&rbsel=0`) // Throw error if backpack was full if (award.match(/Backpack full,.*?not awarded/i)){ alert(`Player's backpack is full. Some items may not have been awarded. Redirecting to award log for more information`) window.location = 'crew_actionlog.php?l=Award%20Item' // Update elements if awarded successfully } else if (award.match('is not available to that user. Reloading vault')){ alert(`An item is not available to that user`); window.location = window.location; } else if (award){ const ids = document.querySelector("#itemsSelectedDiv").innerHTML.match(/v_item_border[0-9]+/g).map(i => i.match(/[0-9]+/i)[0]) document.querySelector("#cvMsg").value = `Awarded ${ids.length} items to ${name}` for (let i = 0; i < ids.length; i++) { const id = ids[i]; document.querySelector(`#v_item_border${id}`).remove() document.querySelector(`#v_selected_${id}`).remove() }; document.querySelector("#selectedCount").innerHTML = '0' document.querySelector("#amuletsSelected").innerHTML = '' document.querySelector("#itemsSelectedDiv").innerHTML = '' await loadingOff(); nonce = await nonceGet(); // Use the data from crew vault post to refresh the item ID numbers to keep them ordered without gaps const parser = new DOMParser(); const doc = parser.parseFromString(award, 'text/html'); const itemRefresh = doc.body.innerHTML.replace(/[\n\r]/g,'').match(/<img alt="[^"]*" class="[^"]*" id="[^"]*" src="[^"]*" style="border:0px;" onmouseover="itempopup\(event,'[0-9]+'\)" onmouseout="kill\(\)" ondblclick="selectAllItem\('[0-9]+'\);kill\(\);" onclick="selectItem\('[0-9]+'\);kill\(\);"><input type="checkbox" name="[^"]*" class="[^"]*" id="[^"]*" value="[0-9]+" style="[^"]*">/g) document.querySelector("#cvItems").innerHTML = itemRefresh.join(''); await cvItemSelectionObserver(); }; } else { document.querySelector("#cvMsg").value = `No items selected` }; }; // Reload nonce async function nonceGet(){ const fetch = await superfetch("crew_vault",true) const nonce = fetch.match(/name="form-nonce" value="([^"]*)"/i)[1]; return nonce; }; // Quick select functions document.querySelectorAll('.cvSelectAllImg').forEach(img => { img.addEventListener('click', async () => { let images; const saImgId = img.id; if (saImgId == "saEthl") { images = ['arheart','saveitem30','arqitem2','arqitem3','saveitem18'] }; if (saImgId == "saNbld") { images = ['5002','5003','5004','5005','5006','5007','5008','5009','5010','5011'] }; if (saImgId == "saSprl") { images = ['UberChallenge'] }; if (saImgId == "saPrfc") { images = ['Perf'] }; if (saImgId == "saExlt") { images = ['exalted'] }; if (saImgId == "saFndr") { images = ['item_holy','item_arcane','item_shadow','item_fire','item_kinetic'] }; if (saImgId == "saDrgn") { images = ['dset_'] }; if (saImgId == "saLffc") { images = ['eearth','eair','ewater','efire','edeath','lifeforce_'] }; if (saImgId == "saSlgm") { images = ['soulgem'] }; if (saImgId == "saWilk") { images = ['itema46','itemz51','itemz110','arqitem1','storeitem4','itema52'] }; if (saImgId == "saWleq") { images = ['InquisitorsHarpoon2','RingofWonders','redkingbelt','blackkingchest'] } if (saImgId == "saTrnk") { images = ['BrutalTrinket','PinkNameTrinket','DarkblueNameTrinket','OrangeNameTrinket','MysteriousTrinket','LuminousTrinket','whitetrinket','enflamedtrinket','agiletrinket'] } if (saImgId == "saGhst") { images = ['ghostly'] } if (saImgId == "saChnc") { images = ['FGR'] } if (saImgId == "saBlck") { images = ['BHR-'] } if (saImgId == "saAmlt") { images = ['AOAChest'] } if (saImgId == "saSmps") { images = ['itema18','itema19','itema21']} if (saImgId == "saTlsm") { images = ['saveitem34','itema7','itemz56','itemz10.jpg','itemz71.jpg'] } if (saImgId == "saVoid") { images = ['voidmaker'] } images.forEach(function(source) { document.querySelector("#cvitems").querySelectorAll('img[src*="' + source + '"]').forEach(function(image) { image.click(); }); }); }); }); // NON-MANAGER: Build HTML } else { cvItems = document.body.innerHTML.replace(/[\n\r]/g,'').match(/<img alt="[^"]*" class="[^"]*" id="[^"]*" src="[^"]*" style="border:0px;" onmouseover="itempopup\(event,'[0-9]+'\)" onmouseout="kill\(\)" ondblclick="selectAllItem\('[0-9]+'\);kill\(\);" onclick="selectItem\('[0-9]+'\);kill\(\);">/g) document.querySelector("#content").innerHTML = ` <div class="row mb-3" style="max-width:100%;"> <div class="pr-1" style="width:50%"> <ul class="list-group text-left"> <li class="list-group-item"><b>Crew Points:</b> ${points}</li> <li class="list-group-item"><b>Sort:</b> <a href="crew_vault?order=1">Alpha</a> / <a href="crew_vault?order=2">Newest</a></li> </ul> </div><div class="pl-1" style="width:50%"> <ul class="list-group text-left"> <li class="list-group-item"><b>Storage:</b> ${capacity[1]} / ${capacity[2]}</li> <li class="list-group-item"><b>Logs:</b> <a href="crew_actionlog.php?l=Award%20Item">Awards</a> / <a href="crew_actionlog.php?l=Delete%20Item">Deleted</a></li> </ul> </div></div> <div style="display:inline-block; max-width: 100%;"> ${cvItems.join('')} </div> ` }; }; async function raidattack(){ // Display all raid results messages const messages = document.querySelectorAll('[id^="message_"]'); messages.forEach(i => { i.style.visibility = 'visible'; }); }; async function pointTransfer(dataPremium){ const parentDiv = document.querySelector("#content-header-row"); const newDiv = document.createElement("div"); newDiv.className = "statbox widget box box-shadow col-12 form-group"; newDiv.setAttribute('style','max-width:700px;margin-top:1rem;') newDiv.innerHTML = ` <h6 style="margin-bottom:0px;">You have <b>${dataPremium.toLocaleString()}</b> transferable points and can transfer up to <b>${(dataPremium * 0.9523809523809524).toFixed(2).toLocaleString()}</b> points</h6> <span id="taxCalc"></span> ` parentDiv.appendChild(newDiv); document.querySelector("#AutoNumber1 > tbody > tr:nth-child(2) > td > input:nth-child(2)").addEventListener('input', function(){ const input = document.querySelector("#AutoNumber1 > tbody > tr:nth-child(2) > td > input:nth-child(2)").value; if (input == '' || input == "0"){ document.querySelector("#taxCalc").innerHTML = '' } else if (input*1.05 > dataPremium){ document.querySelector("#taxCalc").innerHTML = '<h5 style="margin-bottom:0px;margin-top:1rem;color:#FF0000;"><b>You do not have enough points</b></h5>' } else { document.querySelector("#taxCalc").innerHTML = `<h5 style="margin-bottom:0px;margin-top:1rem;">${(input*1.05).toLocaleString()} points will be transferred</h5>` }; }) } // God status page rebuild async function godstatus() { // Styling GM_addStyle (` #godStatusDiv{width:1030px;text-align:left;} div.godStatusTile{display:inline-block;height:250px;overflow:hidden;box-shadow: 0 0 5px rgba(0, 0, 0, 1);margin:2px;text-align:center;} div.godStatusTile > img{width:250px;height:250px;} div.godStatusText{width:220px;position:relative;left:15px;top:-175px;background:#000000;padding:15px;border-radius:15px;font-size:14px;opacity:0.9;width:220px;box-shadow: 0 0 3px rgba(0, 0, 0, 1);} `); // Match all god kills from god status page const allRecentGodKills = document.querySelector("#content").innerHTML.match(/<a href="raidattack\.php\?raidid=[0-9]+">.*?<\/a>[\n\r].*[\n\r].*[\n\r].*[\n\r].*[\n\r].*[\n\r].*/g) // Loop through all results, get god image and total damage from each raid, and build object const raids = await Promise.all(allRecentGodKills.map(async (endpoint) => { const godName = endpoint.match(/<a href="raidattack\.php\?raidid=[0-9]+">(.*?)<\/a>/i)[1]; const raidId = endpoint.match(/<a href="raidattack\.php\?raidid=([0-9]+)">/i)[1]; const raidTime = endpoint.replace(/\s+/g,'').match(/([0-9]+:[0-9]+:[0-9]+)<\/td>/i)[1]; const crewName = endpoint.match(/<a href="crew_profile\.php\?id=[0-9]+">(.*?)<\/a>/i)[1]; const crewId = endpoint.match(/<a href="crew_profile\.php\?id=([0-9]+)">/i)[1]; const data = await superfetch(endpoint.match(/raidattack\.php\?raidid=[0-9]+/i)); const items = data.match(/<a href="#" onmouseover="popup\(event,'.*?'\)" onmouseout="kill\(\)">.*?<\/a>/i) ? data.match(/<a href="#" onmouseover="popup\(event,'.*?'\)" onmouseout="kill\(\)">.*?<\/a>/i) : `<a href="#" onmouseout="kill()">0 items</a>`; const totalDamage = parseInt((data.replace(/,/g, '').match(/<i>Total Attacker Damage: ([0-9]+)<\/i>/i) || [0,0])[1]) const godImgHtml = data.replace(/[\n\r]/g,'').replace(/\s+/g,'').match(/<divclass="defenderimageskinborderd-flexjustify-content-centeralign-items-centermb-3"><imgsrc="(.*?)"><\/div>/i)[1] return [godName, { damage: totalDamage, image: `<img src="${godImgHtml}">`, crew: crewName, crewId, god: godName, raidId, time: raidTime, items: items }]; })); const allGods = Object.fromEntries(raids); // Sort list of god data by total damage to display most damage first const sortedGods = Object.fromEntries(Object.entries(allGods).sort(([, a], [, b]) => b.damage - a.damage)); // Parse through object and build array of new HTML content let newGodStatusContent = [] for (const [god, { damage, image, crew, crewId, raidId, time, items }] of Object.entries(sortedGods)) { newGodStatusContent.push(` <div class="godStatusTile"> ${image}<br> <div class="godStatusText"> <a href="raidattack.php?raidid=${raidId}">${god.replace(/(,|the|The|of).*/gi, '')}</a><br> <a href="crew_profile.php?id=${crewId}">${crew.substring(0, 20)}</a><br> Time: ${time}<br> Damage: ${damage.toLocaleString()}<br> ${items.toString().replace('onmouseout="kill()">','onmouseout="kill()">Found ')} </div> </div>`) }; const content = newGodStatusContent.map((element, index) => (index + 1) % 4 === 0 ? element + '<br>' : element).join(' '); document.querySelector("#content").innerHTML = `<div id="godStatusDiv">${content}</div>`; }; // Blacksmith page async function blacksmith(server,serverNo,rgaName,charId){ var existingDiv = document.querySelector('.col-12.col-xl-6.mb-3.mb-xl-0'); var newDiv = document.createElement('div'); newDiv.innerHTML = `<p> <div id="itemGems"></div> ` // Append the new div element as a child of the existing div existingDiv.appendChild(newDiv); if (window.location.href.match(/itemid=[0-9]+/i) && !window.location.href.match('morphtarget')){ const iid = window.location.href.match(/itemid=([0-9]+)/i)[1]; const item = await superfetch(`item_rollover.php?id=${iid}`); const rarity = item.match(/color:#([A-Za-z0-9]+)"/i)[1]; const gems = 4-(item.match(/\/images\/gemslot2\.jpg/) ? item.match(/\/images\/gemslot2\.jpg/g).length : 0); const upgrades = await info("Cost to Full Gem"); // Cost to Gem returns the cost to full gem so subtract the index of +1 from the cost to full gem to get the cost to +1 gem const cost = upgrades[rarity][gems] - upgrades[rarity][gems+1]; // Is the selected item player bound? const typeOfPoints = item.match(/\[Player Bound\]/i) ? "pb points" : "prem points" // Gem calculator const i = await mvItemSpec(iid); const name = i.name.replace(/<.*?>/g,'').replace(/,/g,''); const slot = i.slot; let atk = parseInt(i.atk.replace(/<.*?>/g,'').replace(/,/g,'')); let hp = parseInt(i.hp.replace(/<.*?>/g,'').replace(/,/g,'')); let rpt = parseInt(i.rpt.replace(/<.*?>/g,'').replace(/,/g,'')); let ept = parseInt(i.ept.replace(/<.*?>/g,'').replace(/,/g,'')); let mr = parseInt(i.mr.replace(/<.*?>/g,'').replace(/,/g,'')); let gemMath = ''; const gemImages = [ { src: "https://torax.outwar.com/images/gem_green1.jpg", gems: 0 }, { src: "https://torax.outwar.com/images/gem_blue2.jpg", gems: 1 }, { src: "https://torax.outwar.com/images/gem_red2.jpg", gems: 2 }, { src: "https://torax.outwar.com/images/gem_white2.jpg", gems: 3 } ]; for (let i = 0; i < gemImages.length; i++) { if (gems <= gemImages[i].gems) { atk *= 1.15; hp *= 1.15; rpt *= 1.15; ept *= 1.15; mr *= 1.15; const stats = `<div align=left><b>${name.toUpperCase()}<br>[SLOT - ${slot.toUpperCase()}]</b><br>+${Math.ceil(atk).toLocaleString()} ATK<br>+${Math.ceil(hp).toLocaleString()} HP<br>+${Math.ceil(rpt).toLocaleString()} rage per hr<br>+${Math.ceil(ept).toLocaleString()} exp per hr<br>+${Math.ceil(mr).toLocaleString()} max rage`; gemMath += `<img src="${gemImages[i].src}" style="height:40px !important;width:40px !important;" onmouseover="statspopup(event,'${stats.replace(/"/g,'')}')" onmouseout="kill()">`; }; }; if (gems !== 4) { document.querySelector("#itemGems").innerHTML = `<h5>GEM CALCULATOR</h5>${gemMath}`; }; }; }; // Gladiator of Loyalty rebuild async function gladiator(charId,profileData){ if (document.body.innerHTML.match(/<h1>Gladiator of Loyalty<\/h1>/i)){ // Styling GM_addStyle(` .mark-count {padding:1px;} .loyalty-glad-table > tbody > tr > td > img {height:25px;width:25px;margin:3px;} .loyalty-glad-table-faction > thead > tr > th {background-color:#000000 !important;color:#FFFFFF !important;} .me-row {box-shadow: inset 0 0 2px 2px rgba(255, 255, 255, 0.4);} .faction-img {height:30px;width:30px;margin-bottom:10px;margin-right:5px;cursor:pointer;} `) const factions = ['Alvar','Delruk','Vordyn']; const stripe1 = ['232756','9B2E17','832069']; const stripe2 = ['2d3b67','5F2216','6F1E58']; for (let i = 0; i < factions.length; i++) { const f = factions[i]; GM_addStyle(` .tier-1-${f},.tier-1-${f} > a {background-color:#${stripe1[i]} !important;color:#FFFFFF !important;} .tier-2-${f},.tier-2-${f} > a {background-color:#${stripe2[i]} !important;color:#FFFFFF !important;} .tier-3-${f},.tier-3-${f} > a {background-color:#${stripe1[i]} !important;color:#FFFFFF !important;} .tier-4-${f},.tier-4-${f} > a {background-color:#${stripe2[i]} !important;color:#FFFFFF !important;} .tier-5-${f},.tier-5-${f} > a {background-color:#${stripe1[i]} !important;color:#FFFFFF !important;} .tier---${f},.tier---${f} > a {background-color:#${stripe2[i]} !important;color:#FFFFFF !important;} `); }; // Self faction const myFaction = profileData.faction; // Get the rankings divs const div = document.querySelector('.grid-container-faction'); // Parse divs const parser = new DOMParser(); const doc = parser.parseFromString(div.innerHTML, 'text/html'); // Convert to array const divs = Array.from(doc.querySelectorAll('.grid-item')).map(div => div.innerHTML.trim()); // Build array of arrays for each line in the rankings table const rankArrays = []; let tempArray = []; divs.forEach((div, index) => { tempArray.push(div); if ((index + 1) % 6 === 0) { rankArrays.push(tempArray); tempArray = []; } }); // Parse rows to make all-factions table const rows = rankArrays.slice(1).map((data, index) => { // Exclude the 2nd and 3rd elements (index 1 and 2) const filteredRowData = data.filter((_, dataIndex) => dataIndex !== 1 && dataIndex !== 2); // Determine the content for the additional column based on the position of the row let newColumn = index < 1 && index >= 0 ? `<img src="https://torax.outwar.com/images/rfury.jpg"><img src="/images/items/triworldgladmark.png"><span class="mark-count">x25</span>` : index < 2 && index >= 1 ? `<img src="https://torax.outwar.com/images/rfury.jpg"><img src="/images/items/triworldgladmark.png"><span class="mark-count">x21</span>` : index < 3 && index >= 2 ? `<img src="https://torax.outwar.com/images/rfury.jpg"><img src="/images/items/triworldgladmark.png"><span class="mark-count">x18</span>` : index < 4 && index >= 3 ? `<img src="https://torax.outwar.com/images/rfury.jpg"><img src="/images/items/triworldgladmark.png"><span class="mark-count">x16</span>` : index < 5 && index >= 4 ? `<img src="https://torax.outwar.com/images/rfury.jpg"><img src="/images/items/triworldgladmark.png"><span class="mark-count">x15</span>` : index < 8 && index >= 5 ? `<img src="/images/items/triworldgladmark.png"><span class="mark-count">x14</span>` : index < 11 && index >= 8 ? `<img src="/images/items/triworldgladmark.png"><span class="mark-count">x13</span>` : index < 14 && index >= 11 ? `<img src="/images/items/triworldgladmark.png"><span class="mark-count">x12</span>` : index < 17 && index >= 14 ? `<img src="/images/items/triworldgladmark.png"><span class="mark-count">x11</span>` : index < 20 && index >= 17 ? `<img src="/images/items/triworldgladmark.png"><span class="mark-count">x10</span>` : index < 22 && index >= 20 ? `<img src="/images/items/triworldgladmark.png"><span class="mark-count">x9</span>` : index < 24 && index >= 22 ? `<img src="/images/items/triworldgladmark.png"><span class="mark-count">x8</span>` : index < 26 && index >= 24 ? `<img src="/images/items/triworldgladmark.png"><span class="mark-count">x7</span>` : index < 28 && index >= 26 ? `<img src="/images/items/triworldgladmark.png"><span class="mark-count">x6</span>` : index < 30 && index >= 28 ? `<img src="/images/items/triworldgladmark.png"><span class="mark-count">x5</span>` : index < 42 && index >= 30 ? `<img src="/images/items/triworldgladmark.png"><span class="mark-count">x4</span>` : index < 50 && index >= 42 ? `<img src="/images/items/triworldgladmark.png"><span class="mark-count">x3</span>` : '' // Style self let style = '' if (filteredRowData[1].match(charId)){ style = `class="me-row"` }; // Create the table row HTML, including the additional column const rowHTML = filteredRowData.map(cellData => `<td>${cellData}</td>`).join(''); return `<tr ${style}>${rowHTML}<td>${newColumn}</td></tr>`; }); // Parse rows to make table rows for each faction const chest = { Vordyn: 'images/items/vordyngladchest.png', Delruk: 'images/items/delrukgladchest.png', Alvar: 'images/items/alvargladchest.png' }; const groupedRows = rankArrays.reduce((groups, rowData) => { if (rowData[2].match(/title=".*?"/)) { const faction = rowData[2].match(/title="(.*?)"/)[1]; // Extract faction name from the 3rd element if (!groups[faction]) { groups[faction] = []; } let img = ''; let tier = ''; const rank = parseInt(rowData[1].replace('.','')); if (rank >= 1 && rank <= 3) { tier = '1' img = `<img src="${chest[faction]}">` } else if (rank >= 4 && rank <= 6) { tier = '2' img = `<img src="${chest[faction]}">` } else if (rank >= 7 && rank <= 9) { tier = '3' img = `<img src="${chest[faction]}">` } else if (rank >= 10 && rank <= 12) { tier = '4' img = `<img src="${chest[faction]}">` } else if (rank >= 13 && rank <= 15) { tier = '5' img = `<img src="${chest[faction]}">` } else { tier = '--' }; // Style self let style = '' if (rowData[3].match(charId)){ style = `class="me-row"` }; // Create a <tr> element with an additional column and push it to the faction's array const trElement = ` <tr ${style}> <td class="tier-${tier}-${faction}">${tier}</td> ${rowData.slice(3).map(cellData => `<td class="tier-${tier}-${faction}">${cellData}</td>`).join('')} <td class="tier-${tier}-${faction}">${img}</td> </tr>`; groups[faction].push(trElement); } return groups; }, {}); // HTML const factionHeaders = '<thead><tr><th>tier</th><th>player</th><th>damage</th><th>atks</th><th>prize</th></tr></thead>' document.querySelector("#content").innerHTML = ` <div class="layout-px-spacing"> <!-- Header --> <div class="widget-content widget-content-area text-center mb-3"> <h1 style="margin-bottom:0rem;">Gladiator of Loyalty</h1> </div> <div class="row justify-content-center"> <!-- Left column --> <div class="col-lg-6 col-md-6 col-sm-6 col-6 layout-spacing layout-spacing"> <!-- Left column widget #1 --> <div class="widget-content widget-content-area text-left mb-3"> <table class="table loyalty-glad-table"> <thead><tr><th>rank</th><th>player</th><th>damage</th><th>atks</th><th>prize</th></tr></thead> ${rows.join('')} </table> </div> </div> <!-- Right column --> <div class="col-lg-6 col-md-6 col-sm-6 col-6 layout-spacing layout-spacing"> <!-- Right column rankings --> <div class="widget-content widget-content-area text-left mb-3"> <img src="https://studiomoxxi.com/moxxibots/factions/a.png" class='faction-img' id="btnAlvar"> <img src="https://studiomoxxi.com/moxxibots/factions/d.png" class='faction-img' id="btnDelruk"> <img src="https://studiomoxxi.com/moxxibots/factions/v.png" class='faction-img' id="btnVordyn"> <i>click the faction logo to see faction-specific rankings</i> <div id="factionRankingsDiv"> </div> </div> </div> ` // Faction table load document.querySelector("#factionRankingsDiv").innerHTML = ` <table class="table loyalty-glad-table loyalty-glad-table-faction"> ${factionHeaders} ${(groupedRows[myFaction] || []).join('')} </table> ` // Faction buttons document.querySelector("#btnAlvar").addEventListener('click', async function(){ document.querySelector("#factionRankingsDiv").innerHTML = ` <table class="table loyalty-glad-table loyalty-glad-table-faction"> ${factionHeaders} ${(groupedRows['Alvar'] || []).join('')} </table> ` }); document.querySelector("#btnDelruk").addEventListener('click', async function(){ document.querySelector("#factionRankingsDiv").innerHTML = ` <table class="table loyalty-glad-table loyalty-glad-table-faction"> ${factionHeaders} ${(groupedRows['Delruk'] || []).join('')} </table> ` }); document.querySelector("#btnVordyn").addEventListener('click', async function(){ document.querySelector("#factionRankingsDiv").innerHTML = ` <table class="table loyalty-glad-table loyalty-glad-table-faction"> ${factionHeaders} ${(groupedRows['Vordyn'] || []).join('')} </table> ` }); }; }; // Homepage rebuild async function home(profileData) { // Styling GM_addStyle (` #content-header-row > div.bio.col-lg-8.col-md-8.col-sm-12.col-12.layout-spacing.layout-spacing > div.widget.widget-chart-one.mb-3{display:none;} #homeTiles{text-align:left;} img.tile-animate{box-shadow: 5px 5px 5px rgba(0, 0, 0, 1);cursor:pointer;transition: .5s ease-out;width:100px;height:100px;border-radius:25px;margin:10px;animation: fadeIn 0.5s ease-in-out forwards, tileanimate 1s infinite;} img.tile-still{box-shadow: 5px 5px 5px rgba(0, 0, 0, 1);cursor:pointer;transition: .5s ease-out;width:100px;height:100px;border-radius:25px;margin:10px;animation: fadeIn 1s ease-in-out forwards;} @keyframes fadeIn {from {opacity: 0;} to {opacity: 1;}} img.tile-still:hover{filter: saturate(250%);opacity:0.5;} @keyframes tileanimate{0% {filter: saturate(100%);} 50% {filter: saturate(250%); } 100% {filter: saturate(100%);}} .rankings-table > tbody> tr > td > a > img {width:200px;height:100px;} .rankings-table > tbody> tr > td{font-size:11px;} .rankings-table{height:340px;overflow:hidden;box-shadow: 0 0 5px rgba(0, 0, 0, 1);} #expChart > tbody > tr > td{padding:4px;font-size:10px;} #expChartContainer{height:1200px;overflow:auto;} tr.me-in-top-20{box-shadow: inset 0 0 2px 2px rgba(255, 255, 255, 0.4);} #homeNewsContainer{padding:26px;} #homeTilesContainer{padding:15px;} #tabSkills > img{width:40px;height:40px;margin:1px;border-radius:5px;border:2px #475254 SOLID;} `); // Parse data from hidden homepage header const homepageHeader = document.querySelector("#content-header-row > div.bio.col-lg-8.col-md-8.col-sm-12.col-12.layout-spacing.layout-spacing > div.widget.widget-chart-one.mb-3").innerHTML const charName = homepageHeader.match(/<h6 class="">(.*?)<\/h6>/i)[1]; const charSpec = homepageHeader.match(/<span class="t-uppercontent">(.*?)<\/span>/i)[1]; // Parse skills (if none, []) const allCast = profileData.skills.images ? profileData.skills.images.join('') : []; // Build new homepage widget elements const menuLeft = document.querySelector("#content-header-row > div.bio.col-lg-8.col-md-8.col-sm-12.col-12.layout-spacing.layout-spacing"); const firstLeftChild = menuLeft.firstChild; const menuRight = document.querySelector("#content-header-row > div.col-lg-4.col-md-4.col-sm-12.col-12.layout-spacing.layout-spacing"); const firstRightChild = menuRight.firstChild; // Header DIV const headerDivContent = ` <div id="homeHeader"> <h3>${charName}</h3> ${charSpec}<p> <div id="tabSkills">${allCast}</div><p style="margin-top:1rem;"> <span id="factionLogo"></span> <p style="margin-top:1rem;"> <b>Thank you for using MoxxiMod v${GM_info.script.version}</b><p> <div id="mmInstalls"><img src="https://studiomoxxi.com/moxximod/loading-gif.gif" height="42px" width="42px"></div> </div> ` const headerDiv = document.createElement('div'); headerDiv.innerHTML = headerDivContent headerDiv.setAttribute('class','widget mb-3'); menuRight.insertBefore(headerDiv, firstRightChild); const faction = profileData.faction; if (faction == "Vordyn"){ document.querySelector("#factionLogo").innerHTML = `<img src="https://studiomoxxi.com/moxxibots/factions/v.png" style="height:100px;width:100px;">` GM_addStyle (`.widget,.widget-content-area{-webkit-box-shadow:0px 0px 3px 3px rgba(252,41,205,0.3);`) } else if (faction == "Alvar"){ document.querySelector("#factionLogo").innerHTML = `<img src="https://studiomoxxi.com/moxxibots/factions/a.png" style="height:100px;width:100px;">` GM_addStyle (`.widget,.widget-content-area{-webkit-box-shadow:0px 0px 3px 3px rgba(0,159,255,0.3);`) } else if (faction == "Delruk"){ document.querySelector("#factionLogo").innerHTML = `<img src="https://studiomoxxi.com/moxxibots/factions/d.png" style="height:100px;width:100px">` GM_addStyle (`.widget,.widget-content-area{-webkit-box-shadow:0px 0px 3px 3px rgba(255,120,39,0.3);`) }; // Tiles DIV const tilesDivContent = ` <div class="widget-content widget-content-area text-left mb-3"> <div id="homeTiles"> <h3 class>Alert Tiles</h3> <div id="homeTilesContainer"> <img src="https://studiomoxxi.com/moxximod/loading-gif.gif" style="height:100px;width:100px;padding:20px"> </div> </div> </div> ` const tilesDiv = document.createElement('div'); tilesDiv.innerHTML = tilesDivContent; menuLeft.insertBefore(tilesDiv, firstLeftChild); // News DIV const fetch = await superfetch('news'); const parse = new DOMParser(); const news = parse.parseFromString(fetch, 'text/html'); const newsHeader = news.querySelector("#content-header-row > div > div:nth-child(1) > div.widget-header > div > div > b").innerHTML const newsContent = news.querySelector("#content-header-row > div > div:nth-child(1) > div.widget-content.widget-content-area.newstext").innerHTML.replace(/<br><br>[\n\r]<img border="0" src="[^"]*">/i,'') const newsDivContent = ` <div class="widget-content widget-content-area text-left mb-3"> <div id="homeNews"> <h3 class>Outwar News</h3> <div id="homeNewsContainer"> ${newsHeader}<br><br> ${newsContent}<br><br> <a href="news">More News</a> </div> </div> </div> ` const newsDiv = document.createElement('div'); newsDiv.innerHTML = newsDivContent; menuLeft.insertBefore(newsDiv, firstLeftChild); // Equipment DIV const equipmentDivContent = profileData.thedude // Rankings DIV const rankingsDiv = document.createElement('div'); rankingsDiv.innerHTML = ` <h3 class>Rankings</h3> <div class="bio-skill-box"><div class="row"> <div class="col-12 col-xl-6"><div class="row"><div class="col-12 mb-3"><div class="d-flex b-skills"> <div class="w-100"> <h5>TOP-10 POWER</h5> <div class="table-responsive" id="rankings-power"> </div> </div></div></div></div></div> <div class="col-12 col-xl-6"><div class="row"><div class="col-12 mb-3"><div class="d-flex b-skills"><div class="w-100"> <h5>TOP-10 ELEMENTAL</h5> <div class="table-responsive" id="rankings-ele"> </div> </div></div></div></div></div> <div class="col-12 col-xl-6"><div class="row"><div class="col-12 mb-3"><div class="d-flex b-skills"><div class="w-100"> <h5>TOP-10 CHAOS</h5> <div class="table-responsive" id="rankings-chaos"> </div> </div></div></div></div></div> <div class="col-12 col-xl-6"><div class="row"><div class="col-12 mb-3"><div class="d-flex b-skills"><div class="w-100"> <h5>TOP-10 MAX RAGE</h5> <div class="table-responsive" id="rankings-mr"> </div> </div></div></div></div></div> ` 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]}<br><img src="https://upload.outwar.com/uploaded/${pic}">` : obj.match(/"name":"([^"]*)"/i)[1] const stat = parseInt(obj.match(/"stat":([0-9]+)/i)[1]).toLocaleString() if (obj.match(/"name":"([^"]*)"/i)[1] == charName){ tableEle += `<tr class="me-in-top-20"><td>${i+1}</td><td><a href="profile?id=${id}">${name}</a></td><td>${stat}</td>` } else { tableEle += `<tr><td>${i+1}</td><td><a href="profile?id=${id}">${name}</a></td><td>${stat}</td>` }; }; for (let i = 0; i < 10; i++) { const obj = arrayPower[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]}<br><img src="https://upload.outwar.com/uploaded/${pic}">` : obj.match(/"name":"([^"]*)"/i)[1] const stat = parseInt(obj.match(/"stat":"([0-9]+)"/i)[1]).toLocaleString() if (obj.match(/"name":"([^"]*)"/i)[1] == charName){ tablePower += `<tr class="me-in-top-20"><td>${i+1}</td><td><a href="profile?id=${id}">${name}</a></td><td>${stat}</td>` } else { tablePower += `<tr><td>${i+1}</td><td><a href="profile?id=${id}">${name}</a></td><td>${stat}</td>` }; }; for (let i = 0; i < 10; i++) { const obj = arrayChaos[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]}<br><img src="https://upload.outwar.com/uploaded/${pic}">` : obj.match(/"name":"([^"]*)"/i)[1] const stat = parseInt(obj.match(/"stat":([0-9]+)/i)[1]).toLocaleString() if (obj.match(/"name":"([^"]*)"/i)[1] == charName){ tableChaos += `<tr class="me-in-top-20"><td>${i+1}</td><td><a href="profile?id=${id}">${name}</a></td><td>${stat}</td>` } else { tableChaos += `<tr><td>${i+1}</td><td><a href="profile?id=${id}">${name}</a></td><td>${stat}</td>` }; }; for (let i = 0; i < 10; i++) { const obj = arrayMr[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]}<br><img src="https://upload.outwar.com/uploaded/${pic}">` : obj.match(/"name":"([^"]*)"/i)[1] const stat = parseInt(obj.match(/"stat":([0-9]+)/i)[1]).toLocaleString() if (obj.match(/"name":"([^"]*)"/i)[1] == charName){ tableMr += `<tr class="me-in-top-20"><td>${i+1}</td><td><a href="profile?id=${id}">${name}</a></td><td>${stat}</td>` } else { tableMr += `<tr><td>${i+1}</td><td><a href="profile?id=${id}">${name}</a></td><td>${stat}</td>` }; }; document.querySelector("#rankings-ele").innerHTML = `<table class="rankings-table table table-striped">${tableEle}</table>` document.querySelector("#rankings-power").innerHTML = `<table class="rankings-table table table-striped">${tablePower}</table>` document.querySelector("#rankings-chaos").innerHTML = `<table class="rankings-table table table-striped">${tableChaos}</table>` document.querySelector("#rankings-mr").innerHTML = `<table class="rankings-table table table-striped">${tableMr}</table>` // Exp chart expChart(); async function expChart(){ const expChart = await superfetch('expchart'); const expChartParse = expChart.replace(/<td.*?>/g,'').replace(/,/g,'').replace(/\s/g,''); const expChartArray = expChartParse.match(/[0-9]+<\/td>[0-9]+<\/td>[0-9]+<\/td>[0-9]+<\/td>[0-9]+/g); function sortByNumericValue(str) { const numericValue = parseInt(str.match(/\d+(?=<\/td>)/)[0]); return numericValue; }; expChartArray.sort((a, b) => sortByNumericValue(a) - sortByNumericValue(b)); let expChartNative = '' for (let i = 0; i < expChartArray.length; i++) { const row = expChartArray[i].replace(/<\/td>/g,'</td><td>') expChartNative += `<tr><td>${row}</td></tr>` }; const expChartRows = expChartNative.replace(/\d+/g, match => parseInt(match, 10).toLocaleString()); const expChartTable = ` <table id="expChart" class="table table-striped"> <thead><tr><th>Lvl</th><th>Experience</th><th>SP</th><th>Atk</th><th>Hp</th></tr></thead> ${expChartNative} </table> ` document.querySelector("#expChartContainer").innerHTML = expChartTable; }; // Reset moxximod button var parentDiv = document.querySelector("#content-header-row > div.col-lg-4.col-md-4.col-sm-12.col-12.layout-spacing.layout-spacing"); var newDiv = document.createElement("div"); newDiv.setAttribute('style','margin-top:1rem;') newDiv.innerHTML = ` <div class="widget widget-table-one mb-3"> <h6>Experience Chart</h6> <div id="expChartContainer"> </div> </div> <div class="widget widget-table-one mb-3"> <button id="mmReset" class="btn-mm">RESET MOXXIMOD</button> </div>` parentDiv.appendChild(newDiv); // Moxximod total install greasy(); async function greasy(){ const fork = await superfetch('https://greasyfork.org/en/users/907800-studiomoxxi'); const dailyInstalls = fork.match(/<dd class="script-list-daily-installs"><span>([0-9]+)<\/span><\/dd>/i)[1] const totalInstalls = fork.match(/<dd class="script-list-total-installs"><span>([0-9]+)<\/span><\/dd>/i)[1] document.querySelector("#mmInstalls").innerHTML = ` Daily installs: ${dailyInstalls}<br> Total installs: ${totalInstalls} ` }; // Misc homepage functions if (tilesDiv.innerHTML.match('Click to max')){ document.querySelector("#maxSupplies").addEventListener("click", maxSupplies); }; // Reset moxximod function document.querySelector('#mmReset').addEventListener('click',function(){ const confirm = window.confirm("Are you sure you want to reset all MoxxiMod settings to default?"); if (confirm) { var allKeys = GM_listValues(); allKeys.forEach(function(key) { GM_setValue(key, ""); }); alert("MoxxiMod has been reset to default."); appsMenuClose(); }; }); }; // World rebuild async function world(profileData,server){ let roomnum = document.querySelector("#roomid_display").innerHTML; if (document.querySelector("#mapHtml").innerHTML){ await worldApply(profileData,server); await worldPathFollower(); }; const targetNode = document.querySelector("#mapHtml"); // Run mutation observer async function contentChangeCallback(mutations) { if (roomnum != document.querySelector("#roomid_display").innerHTML){ roomnum = document.querySelector("#roomid_display").innerHTML worldApply(profileData,server); }; await worldPathFollower(); }; const observer = new MutationObserver(contentChangeCallback); observer.observe(targetNode, { childList: true, subtree: true }); }; async function worldApply(profileData,server){ const profilepic = profileData.profilepic; // Styling GM_addStyle(` body{overflow-y: scroll;} #content{display:none !important;} #mapTable{box-shadow: 0 0 5px rgba(255, 255, 255, 1);border-radius: 20px !important;} td.map-tile > div {height:44px !important;width:44px !important;} td.map-tile {background-size: cover !important;} #new-content{position:relative;width:50%;flex-grow:8;margin-top:130px;margin-bottom:0;margin-left:212px;max-width:1300px;transition: .6s;} img[src*="YAH.gif"] {margin-top:8px !important;height:25px;width:25px;content: url("${profilepic}") !important;border-radius:10px;box-shadow:0 5px 5px 0 rgba(0,0,0,1) !important;animation: player-icon 1s infinite;} @keyframes player-icon{0% {filter: saturate(100%);} 50% {filter: saturate(250%); } 100% {filter: saturate(100%);}} img[src*="owpath.png"] {margin-top:5px !important;height:35px;width:35px;} .w-3{-ms-flex: 0 0 27%;flex: 0 0 27%;max-width: 27%;} .w-4{-ms-flex: 46%;flex: 0 0 46%;max-width: 46%;} #liveWorld{width:100%;font-size:16px;cursor:pointer;font-family:monospace;height:50px;} #liveSearch{width:100%;font-size:16px;font-family:monospace;height:50px;} .mob-win{border: 1px solid #32CD32 !important;} .mob-loss{border: 1px solid #FF0000 !important;} #worldSkillsDiv > img {height: 20px;width: 20px;margin: 4px;border-radius: 5px;box-shadow: 0 0 3px rgba(0, 0, 0, 0.5);background: #000000;cursor: pointer;transition: .5s ease-out;} .findMobAndAtkAll {padding:2px;border-top-left-radius: inherit;border-top-right-radius:inherit;font-size:16px;font-weight:700;cursor:pointer;letter-spacing:2px;} #questLinks{margin-bottom:1rem;} #questInfoDiv{position:fixed;transform:translate(-50%, -50%);top:50%;left:50%;width:768px;height:80%;transition:opacity 0.25s ease;} #sb-container{display:none !important;} #questFrame{transition: .35s ease;height:0px;border-radius:10px;} img.world-directional{height:30px;width:30px;margin-right:10px;margin-left:10px;} #levelUpDiv{background-image: url("landing/levelupbg.jpg");} #qHelp{position:absolute;bottom:1rem;right:1rem;} #quest_helper_content{height:400px;width:600px;overflow:auto;} `) // Variables const map = document.querySelector("#mapHtml").innerHTML.replace(/td style/g,'td class="map-tile" style').replace('table','table id="mapTable"') const num = document.querySelector("#roomid_display").innerHTML const name = document.querySelector("#span_roomName").innerHTML.replace(/-/g,''); const mobs = document.querySelector("#roomDetails").innerHTML.replace(/<h6 class="[^"]*" style="[^"]*"><a rel="[^"]*" class="sbox" href="[^"]*">/g,`<h6 class="spawnTitle text-left" style="letter-spacing:inherit">`); // Equipment const eq = profileData.thedude; const proPic = profileData.profilepic; // Skills const skills = profileData.skills.images || []; // New content if (!document.querySelector("#new-content")){ var newContent = document.createElement('div'); newContent.id = 'new-content'; var containerDiv = document.getElementById('container'); var firstChild = containerDiv.firstChild; containerDiv.insertBefore(newContent, firstChild); }; // Build HTML document.querySelector("#new-content").innerHTML = ` <div class="row justify-content-center"> <div class="w-3 col-lg-6 col-md-6 col-sm-12 col-12 layout-spacing px-1" style="min-width:355px"> <div class="widget mb-2"> ${map}<br> <h5>${name} [${num}]</h5> <i>click on the map to move rooms<br><br></i> <button class="btn-mm" id="pathFollowBtn">AUTO MOVE ON PATH</button><br><br> <a href="world.php?room=11">Click to Teleport to Room 11</a><br> </div> <div class="widget mb-2" id="questHelperDiv" style="padding:10px;max-height:400px;overflow:auto;"> <a href="javascript:void(0);" id="questHelper">QUEST HELPER</a> </div> </div> <div class="w-4 col-lg-6 col-md-6 col-sm-12 col-12 layout-spacing px-1"> <div class="widget mb-2" style="padding:5px;"> <div id="liveSpan" style="display:flex;justify-content:center;"><input id="liveWorld" type="text" class="form-control-new" autocomplete="off""></div> </div> <div class="widget mb-2" id="mobList"> <div id="findMobDiv" class="list-group-item list-group-item-action spawnRow findMobAndAtkAll" style="width:calc(50% - 3px);display:inline-block;">FIND A MOB</div> <div id="atkAllDiv" class="list-group-item list-group-item-action spawnRow findMobAndAtkAll" style="width:calc(50% - 3px);display:inline-block;">ATK ALL MOBS</div> <span id="mobSpan">${mobs}</span> </div> </div> <div class="w-3 col-lg-6 col-md-6 col-sm-12 col-12 layout-spacing px-1" style="min-width:355px" id="worldRightColumn"> <div class="widget mb-2" id="worldSkillsDiv" style="padding:10px"> <h6>Active Skills & Potions</h6> <div id="hasteDiv" style="padding-bottom:10px"><img src="https://studiomoxxi.com/moxximod/loading-gif.gif" height="20px" width="20px"></div> ${skills.join('')} </div> <div class="widget mb-2"> ${eq} </div> </div> ` // Modify map for touch movement const mapTiles = document.querySelectorAll(".map-tile"); mapTiles[17].addEventListener('click',async function(){gotoRoom(northRoom,curRoom)}); mapTiles[23].addEventListener('click',async function(){gotoRoom(westRoom,curRoom)}); mapTiles[24].id = "currentRoomTile" mapTiles[25].addEventListener('click',async function(){gotoRoom(eastRoom,curRoom)}); mapTiles[31].addEventListener('click',async function(){gotoRoom(southRoom,curRoom)}); mapTiles[17].style.cursor = "pointer" mapTiles[23].style.cursor = "pointer" mapTiles[25].style.cursor = "pointer" mapTiles[31].style.cursor = "pointer" // Attack button functions const atkLinks = document.querySelectorAll("#mobSpan > ul > li > div > div > a[href*='attackid']"); if (atkLinks.length > 0){ const atkIds = []; atkLinks.forEach(function(atk){ const atkid = atk.href.match(/attackid=[A-Za-z0-9]+/i); let atkUrl = `somethingelse.php?${atkid}` // Add userspawn=1 if mob was userspawn mob if (atk.href.match('userspawn=1')){ atkUrl += '&userspawn=1' }; atkIds.push(atkid); atk.href = "javascript:void(0);" atk.removeAttribute('target') // Add event listener to attack mob buttons atk.addEventListener('click', async function(){ // Attack mob const attack = await superfetch(atkUrl,true); const mobName = attack.match(/var defender_name = "([^"]*)"/i)[1]; const mobHp = parseInt(attack.match(/defender_health_start = ([0-9]+)/i)[1]); const mobBaseDmgTaken = (attack.match(/defender_taken\[[0-9]+\] = '[0-9]+'/g) || []).reduce((sum, match) => sum + parseInt(match.match(/'([0-9]+)'/)[1]), 0); const mobHolyDmgTaken = (attack.match(/defender_elemental_taken\[[0-9]+\]\[[0-9]+\] = new Damage\([0-9]+, 'holy'\)/g) || []).reduce((sum, match) => sum + parseInt(match.match(/\(([0-9]+)/)[1]), 0); const mobArcnDmgTaken = (attack.match(/defender_elemental_taken\[[0-9]+\]\[[0-9]+\] = new Damage\([0-9]+, 'arcane'\)/g) || []).reduce((sum, match) => sum + parseInt(match.match(/\(([0-9]+)/)[1]), 0); const mobShadDmgTaken = (attack.match(/defender_elemental_taken\[[0-9]+\]\[[0-9]+\] = new Damage\([0-9]+, 'shadow'\)/g) || []).reduce((sum, match) => sum + parseInt(match.match(/\(([0-9]+)/)[1]), 0); const mobFireDmgTaken = (attack.match(/defender_elemental_taken\[[0-9]+\]\[[0-9]+\] = new Damage\([0-9]+, 'fire'\)/g) || []).reduce((sum, match) => sum + parseInt(match.match(/\(([0-9]+)/)[1]), 0); const mobKinkDmgTaken = (attack.match(/defender_elemental_taken\[[0-9]+\]\[[0-9]+\] = new Damage\([0-9]+, 'kinetic'\)/g) || []).reduce((sum, match) => sum + parseInt(match.match(/\(([0-9]+)/)[1]), 0); const mobChosDmgTaken = (attack.match(/defender_elemental_taken\[[0-9]+\]\[[0-9]+\] = new Damage\([0-9]+, 'chaos'\)/g) || []).reduce((sum, match) => sum + parseInt(match.match(/\(([0-9]+)/)[1]), 0); const mobRemainingHp = Math.max((mobHp-mobBaseDmgTaken-mobHolyDmgTaken-mobArcnDmgTaken-mobShadDmgTaken-mobFireDmgTaken-mobKinkDmgTaken-mobChosDmgTaken),0); const mobPic = atk.parentNode.parentNode.parentNode.querySelector('img.spawnImage').outerHTML.match(/src="([^"]*)"/i)[1]; // Create attack result variables let liveTxt = '' let liveClass; if (attack.match(/var successful = 1/i)){ liveClass = 'mob-win'; liveTxt += `Won vs ${mobName}` // If spawning a rare spawn mob if (attack.match('steps out of the shadows')){ window.location = 'world' }; // If item is dropped if (attack.match(/<b>WIN: Found .*?<\/b>/i)){ const item = attack.match(/<b>WIN: Found (.*?)<\/b>/i)[1] liveTxt += ` found ${item}!` }; // If kill count condition if (attack.match(/<\/b>[0-9]+\/[0-9]+ killed<br>/i)){ const count = attack.match(/<\/b>([0-9]+\/[0-9]+) killed<br>/i)[1] liveTxt += ` ${count} killed` }; liveTxt += ' (click for info)' atk.parentNode.parentNode.parentNode.remove(); } else { liveClass = 'mob-loss' liveTxt += `Lost vs ${mobName}: ${(mobRemainingHp/mobHp*100).toFixed(2)}% (click for info)` }; // Rebuild live span textbox with new content and class in order to prevent duplicate event listeners document.querySelector("#liveSpan").innerHTML = `<input id="liveWorld" type="text" class="${liveClass} form-control-new" autocomplete="off" value="${liveTxt}">` document.querySelector("#liveWorld").addEventListener('click', async function(){ await worldMobAtkInfo(attack,proPic,mobPic) }); }); }); // Attack all button document.querySelector("#atkAllDiv").addEventListener('click', async function(){ await loadingOverlay(); for (let i = 0; i < atkIds.length; i++) { const atkid = atkIds[i]; await superfetch(`somethingelse.php?${atkid}`); }; window.location = 'world'; }); }; if (mobs.length <= 58){ document.querySelector("#liveWorld").value = "No mobs found in this room" }; // Quest button functions const qLinks = document.querySelectorAll('#mobSpan img[src="/landing/questicon.png"]'); if (qLinks.length > 0){ // Modify elements for each orange talk button qLinks.forEach(function(q){ const talk = q.parentNode.outerHTML.match(/href="([^"]*)"/i)[1].replace(/amp;/g,''); q.parentNode.href = "javascript:void(0);" q.addEventListener('click', mobTalk); // Mob talk function async function mobTalk(){ const mob = await superfetch(talk,true); const quests = mob.replace(/\.php/g,'').match(/<a href="mob_talk\?id=[0-9]+&stepid=[0-9]+&userspawn=.*?&questid=[0-9]+"><img border="0" src="\/images\/button_quest_up\.gif" hspace="2" align="absmiddle"><font size="3"><b>.*?<\/b><\/font><\/a>/g).map(q => q.replace('<img border=\"0\" src=\"/images/button_quest_up.gif\" hspace=\"2\" align=\"absmiddle\">','').replace(/<a href/g,'<span class="questLink" style="cursor:pointer;" alt').replace(/<\/a>/,'</span>')); document.querySelector("#liveSpan").innerHTML = ` <div id="questLinks" style="margin-top:1rem;"> <h5>Available Quests</h5><br> ${quests.join('<br>')} </div> ` // Build for each quest link found in the quest mob const linkArray = document.querySelectorAll('.questLink'); linkArray.forEach(function(quest){ quest.addEventListener('click', async function(){ const questLink = quest.outerHTML.match(/alt="([^"]*)"/i)[1].replace(/amp;/g,''); document.querySelector("#liveSpan").innerHTML = ` <iframe src="${questLink}" tabindex="-1" id="questFrame" style="border:0px #ffffff none;opacity:0;" scrolling="no" frameborder="1" marginheight="0px" height="500px" width="100%"></iframe> <div id="qHelp"></div> ` document.getElementById('questFrame').addEventListener('load', function() { // Access the iframe content const iframe = document.querySelector("#questFrame"); if (iframe) { // Display iframe content iframe.style.opacity = '1'; // Display iframe content // Modify back link to re-run mob talk function and remove href var backLink = iframe.contentDocument.querySelector('a[href*="mob.php?id="]'); if (backLink) { backLink.href = "javascript:void(0);" backLink.addEventListener('click', mobTalk) }; // Set margin top to 0 instead of 9px const content = iframe.contentDocument.querySelector("#content-header-row") if (content){ content.setAttribute('style','margin-top:0px;') }; // Set the height of the iframe equal to the content const iframeDocument = iframe.contentDocument || iframe.contentWindow.document; iframe.style.height = iframeDocument.body.scrollHeight + 'px'; }; // Quest help button const questId = questLink.match(/questid=([0-9]+)/i)[1]; document.querySelector("#qHelp").innerHTML = ` <a href="javascript:void(0);" id="qHelpBtn"> <img src="images/questwiki.jpg" onmouseover="statspopup(event,'Quest Helper')" onmouseout="kill()"> </a> ` document.querySelector("#qHelpBtn").addEventListener('click', async function(){ const quest = await superfetch(`show_quest.php?quest=${questId}`) createWindow("Quest Helper", "quest_helper", 600, 250, 0); document.querySelector("#quest_helper_content").innerHTML = quest.replace(/solid #ffffff/g,'solid #333333') }); }); }); }); }; }); }; // Haste button const haste = await superfetch('skills_info.php?id=3024'); // If haste is available if (haste.match(/value="Cast Skill"/i)){ document.querySelector("#hasteDiv").innerHTML = `<a href="javascript:void(0);" id="castHaste">Click to Cast Haste</a>` document.querySelector("#castHaste").addEventListener('click', async function(){ await superpost('cast_skills.php?C=4','castskillid=3024&cast=Cast+Skill'); window.location = 'world' }); // If haste is recharging } else if (haste.match(/This skill is recharging\. [0-9]+ minutes remaining\./i)){ const time = haste.match(/This skill is recharging\. ([0-9]+) minutes remaining\./i)[1] document.querySelector("#hasteDiv").innerHTML = `Haste is ready in ${time} minutes` // If haste isn't trained } else if (haste.match(/You have not learned this skill yet/i)){ document.querySelector("#hasteDiv").innerHTML = `Haste is not trained` }; // Quest helper document.querySelector("#questHelper").addEventListener('click', function(){ const quests = document.querySelector(".widget.widget-chart-two").innerHTML document.querySelector("#questHelperDiv").innerHTML = quests.replace(/<div id="mobsearch" style="font-weight: bold; text-align: center; margin-top: 10px;"><a id="[^"]*" href="#"><img src="[^"]*" onmouseover="[^"]*" onmouseout="[^"]*" border="0"><\/a><\/div>/i,'').replace(/<h5 class="">QUEST HELPER <\/h5>/i,''); }); // Find a mob document.querySelector("#findMobDiv").addEventListener('click', function(){ // Build the search box document.querySelector("#liveSpan").innerHTML = ` <input id="liveSearch" type="text" class="form-control-new" autocomplete="off" placeholder="Enter mob name..."> <button class="btn-mm" id="mobSearchBtn">SEARCH</button> ` document.querySelector("#liveSearch").select(); // Add listeners to textbox and button document.querySelector("#mobSearchBtn").addEventListener('click', worldMobSearch); document.querySelector("#liveSearch").addEventListener("keydown", function(event) { if (event.key === "Enter") { worldMobSearch() } }); // World mob search function async function worldMobSearch(){ const searchInput = document.querySelector("#liveSearch").value.replace(/ /g,'+'); const mobSearch = await superfetch(`https://${server}.outwar.com/mob_search.php?searchterm=${searchInput}`); document.querySelector("#liveSpan").innerHTML = mobSearch.replace(/href/g,'alt'); const searchResults = document.querySelectorAll("#return_data > p > a"); searchResults.forEach(async r => { r.setAttribute('style','cursor:pointer;') r.addEventListener('click', async function(){ const apply = r.outerHTML.match(/alt="([^"]*)"/i)[1]; await superfetch(apply); window.location = 'world'; }); }); }; }); // Level up button var levelup = window.getComputedStyle(document.querySelector("#levelup")).getPropertyValue("display"); if (levelup == "block"){ var worldRightColumn = document.getElementById("worldRightColumn"); var newDiv = document.createElement("div"); newDiv.classList.add('widget'); newDiv.classList.add('mb-2'); newDiv.id = "levelUpDiv" newDiv.innerHTML = ` <img src="landing/levelupcenter.jpg" style="height:100%;width:100%;border-radius:8px;cursor:pointer;" id="lvlUpBtn"> `; worldRightColumn.insertBefore(newDiv, worldRightColumn.firstChild); document.querySelector("#lvlUpBtn").addEventListener('click',async function(){ const check = confirm('Are you sure you want to level up?'); if (check){ await superfetch('levelup'); window.location = 'world'; }; }); }; }; async function worldPathFollower(){ // Fetch interceptor const originalFetch = window.fetch; const originalXHR = XMLHttpRequest.prototype.open; const targetRegex = /quest_help\.php|mob_search\.php\?target=\d+/; function intercept(url) { if (targetRegex.test(url)) { GM_setValue('pathBuildingUrl', url); }; }; window.fetch = new Proxy(originalFetch, { apply(target, thisArg, args) { intercept(args[0]); return Reflect.apply(target, thisArg, args); } }); XMLHttpRequest.prototype.open = function(method, url, ...rest) { intercept(url); return originalXHR.apply(this, [method, url, ...rest]); }; // Get the room ajax data const ajax = await superfetch('ajax_changeroomb', true); // If the quest path dot was found on the room ajax data if (ajax.match(/"questHelpData":"[^"]*"/i)){ // Make button pulsate when a path is found GM_addStyle(` @keyframes glow { 0% {box-shadow: 0 0 0px rgba(255, 255, 255, 0.0);} 2% {box-shadow: 0 0 0px rgba(255, 255, 255, 0.25);} 50% {box-shadow: 0 0 10px rgb(255, 255, 255);} 98% {box-shadow: 0 0 0px rgba(255, 255, 255, 0.25);} 100% {box-shadow: 0 0 0px rgba(255, 255, 255, 0.0);} } .path-follow-pulse {animation: glow 2.5s infinite;} `) document.querySelector("#pathFollowBtn").classList.add("path-follow-pulse") // On button click document.querySelector("#pathFollowBtn").addEventListener('click', async () => { const string = await mmplus('AuthCheck|rganame'); if (!string.match('Full')){ return; }; await loadingOverlay(); await pathMove(ajax); }); // Path move function to be called in loop async function pathMove(ajax){ const direction = ajax.match(/"questHelpData":"dpadcenter_(.*?).jpg"/i)[1]; const regex = new RegExp(`"${direction}":"([0-9]+)"`); const nextRoom = ajax.match(regex)[1]; const move = await superfetch(`ajax_changeroomb.php?room=${nextRoom}`, true); // Refresh page to terminate the mover if trying to enter a locked room if (move.match('teleport to your home bar')){ window.location = window.location; }; // Call the path move function again if the dot is still found if (move.match(/"questHelpData":"[^"]*"/i)){ await pathMove(move); } else { const success = move.match(/"3_3":\{"qm":3,"image":[0-9]+\}/i) ? true : false; if (success){ GM_deleteValue('pathBuildingUrl'); window.location = window.location; } else { // Move failed due to outwar pathing issues. Resending the path building request and trying again document.querySelector("#loadingOverlayText").innerHTML = 'Outwar pathing failed. Resending the request to build a path and trying again.' let attempt = 1; await rebuildPath(); async function rebuildPath(){ attempt += 1; const pathBuildingUrl = GM_getValue('pathBuildingUrl'); await superfetch(pathBuildingUrl); const ajaxNew = await superfetch('ajax_changeroomb', true); if (ajaxNew.match(/"questHelpData":"[^"]*"/i)){ await pathMove(ajaxNew); } else if (attempt <= 10){ rebuildPath(); } else { // Critical failure. Path unable to be restored. Refreshing page. window.location = window.location; }; }; }; }; }; }; }; async function worldMobAtkInfo(attack,proPic,mobPic){ // Styling GM_addStyle(` #container{transition: opacity 0.25s ease;} #atkInfoDiv{position:fixed;transform:translate(-50%, -25%);top:25%;left:50%;width:768px;height:80%;transition:opacity 0.25s ease;} .atk-info-widget{width:45%;display:inline-block;margin:1rem;height:100%;overflow:auto;} img.info-img{width:300px;height:150px;box-shadow: 0 0 5px rgba(255, 255, 255, 1);} `) // Damage variables const plrHp = parseInt(attack.match(/var attacker_health_start = ([0-9]+)/i)[1]); const mobHp = parseInt(attack.match(/var defender_health_start = ([0-9]+)/i)[1]); const plrBaseDmgTaken = (attack.match(/attacker_taken\[[0-9]+\] = '[0-9]+'/g) || []).reduce((sum, match) => sum + parseInt(match.match(/'([0-9]+)'/)[1]), 0); const plrHolyDmgTaken = (attack.match(/attacker_elemental_taken\[[0-9]+\]\[[0-9]+\] = new Damage\([0-9]+, 'holy'\)/g) || []).reduce((sum, match) => sum + parseInt(match.match(/\(([0-9]+)/)[1]), 0); const plrArcnDmgTaken = (attack.match(/attacker_elemental_taken\[[0-9]+\]\[[0-9]+\] = new Damage\([0-9]+, 'arcane'\)/g) || []).reduce((sum, match) => sum + parseInt(match.match(/\(([0-9]+)/)[1]), 0); const plrShadDmgTaken = (attack.match(/attacker_elemental_taken\[[0-9]+\]\[[0-9]+\] = new Damage\([0-9]+, 'shadow'\)/g) || []).reduce((sum, match) => sum + parseInt(match.match(/\(([0-9]+)/)[1]), 0); const plrFireDmgTaken = (attack.match(/attacker_elemental_taken\[[0-9]+\]\[[0-9]+\] = new Damage\([0-9]+, 'fire'\)/g) || []).reduce((sum, match) => sum + parseInt(match.match(/\(([0-9]+)/)[1]), 0); const plrKinkDmgTaken = (attack.match(/attacker_elemental_taken\[[0-9]+\]\[[0-9]+\] = new Damage\([0-9]+, 'kinetic'\)/g) || []).reduce((sum, match) => sum + parseInt(match.match(/\(([0-9]+)/)[1]), 0); const plrChosDmgTaken = (attack.match(/attacker_elemental_taken\[[0-9]+\]\[[0-9]+\] = new Damage\([0-9]+, 'chaos'\)/g) || []).reduce((sum, match) => sum + parseInt(match.match(/\(([0-9]+)/)[1]), 0); const plrRemainingHp = Math.max((plrHp-plrBaseDmgTaken-plrHolyDmgTaken-plrArcnDmgTaken-plrShadDmgTaken-plrFireDmgTaken-plrKinkDmgTaken-plrChosDmgTaken),0); const mobBaseDmgTaken = (attack.match(/defender_taken\[[0-9]+\] = '[0-9]+'/g) || []).reduce((sum, match) => sum + parseInt(match.match(/'([0-9]+)'/)[1]), 0); const mobHolyDmgTaken = (attack.match(/defender_elemental_taken\[[0-9]+\]\[[0-9]+\] = new Damage\([0-9]+, 'holy'\)/g) || []).reduce((sum, match) => sum + parseInt(match.match(/\(([0-9]+)/)[1]), 0); const mobArcnDmgTaken = (attack.match(/defender_elemental_taken\[[0-9]+\]\[[0-9]+\] = new Damage\([0-9]+, 'arcane'\)/g) || []).reduce((sum, match) => sum + parseInt(match.match(/\(([0-9]+)/)[1]), 0); const mobShadDmgTaken = (attack.match(/defender_elemental_taken\[[0-9]+\]\[[0-9]+\] = new Damage\([0-9]+, 'shadow'\)/g) || []).reduce((sum, match) => sum + parseInt(match.match(/\(([0-9]+)/)[1]), 0); const mobFireDmgTaken = (attack.match(/defender_elemental_taken\[[0-9]+\]\[[0-9]+\] = new Damage\([0-9]+, 'fire'\)/g) || []).reduce((sum, match) => sum + parseInt(match.match(/\(([0-9]+)/)[1]), 0); const mobKinkDmgTaken = (attack.match(/defender_elemental_taken\[[0-9]+\]\[[0-9]+\] = new Damage\([0-9]+, 'kinetic'\)/g) || []).reduce((sum, match) => sum + parseInt(match.match(/\(([0-9]+)/)[1]), 0); const mobChosDmgTaken = (attack.match(/defender_elemental_taken\[[0-9]+\]\[[0-9]+\] = new Damage\([0-9]+, 'chaos'\)/g) || []).reduce((sum, match) => sum + parseInt(match.match(/\(([0-9]+)/)[1]), 0); const mobRemainingHp = Math.max((mobHp-mobBaseDmgTaken-mobHolyDmgTaken-mobArcnDmgTaken-mobShadDmgTaken-mobFireDmgTaken-mobKinkDmgTaken-mobChosDmgTaken),0); const plrBLocks = attack.match(/attacker_taken\[[0-9]+\] = 'block'/) ? attack.match(/attacker_taken\[[0-9]+\] = 'block'/g).length : 0; // Faction variables const parser = new DOMParser(); const dom = parser.parseFromString(attack, 'text/html'); const factionBoxLeft1 = dom.querySelector('.float-div-left.faction-box-1'); const factionBoxRight1 = dom.querySelector('.float-div-right.faction-box-1'); const myLoyalty = factionBoxLeft1 ? parseInt(factionBoxLeft1.innerHTML.match(/([0-9]+) \/ [0-9]+/i)[1]) : 0; const maxLoyalty = factionBoxLeft1 ? parseInt(factionBoxLeft1.innerHTML.match(/[0-9]+ \/ ([0-9]+)/i)[1]) : 0; const multiplier = factionBoxRight1 ? parseInt(factionBoxRight1.innerHTML.match(/[0-9]+/i)) * 0.01 : 0; const loyalty = `${(myLoyalty * multiplier * 100)}% / ${(maxLoyalty * multiplier * 100)}%` // HTML content const content = ` <div class="widget atk-info-widget"> <center><img src="${proPic}" class="info-img"></center><br> <table class="table table-striped mb-3"> <tr><td>Starting Hit Points</td><td>${plrHp.toLocaleString()}</td></tr> <tr><td>Base Damage Taken</td><td>${plrBaseDmgTaken.toLocaleString()}</td></tr> <tr><td><font color="#00FFFF">Holy Damage Taken</font></td><td>${plrHolyDmgTaken.toLocaleString()}</td></tr> <tr><td><font color="#FFFF00">Arcane Damage Taken</font></td><td>${plrArcnDmgTaken.toLocaleString()}</td></tr> <tr><td><font color="#7e01bc">Shadow Damage Taken</font></td><td>${plrShadDmgTaken.toLocaleString()}</td></tr> <tr><td><font color="#FF0000">Fire Damage Taken</font></td><td>${plrFireDmgTaken.toLocaleString()}</td></tr> <tr><td><font color="#00FF00">Kinetic Damage Taken</font></td><td>${plrKinkDmgTaken.toLocaleString()}</td></tr> <tr><td><font color="#f441be">Chaos Damage Taken</font></td><td>${plrChosDmgTaken.toLocaleString()}</td></tr> <tr><td>Hit Points Remaining</td><td>${plrRemainingHp.toLocaleString()}</td></tr> <tr><td>Successful Blocks</td><td>${plrBLocks.toLocaleString()}</td></tr> <tr><td>Faction Bonus</td><td>${loyalty}</td></tr> </table> </div> <div class="widget atk-info-widget"> <center><img src="${mobPic}" class="info-img"></center><br> <table class="table table-striped"> <tr><td>Starting Hit Points</td><td>${mobHp.toLocaleString()}</td></tr> <tr><td>Base Damage Taken</td><td>${mobBaseDmgTaken.toLocaleString()}</td></tr> <tr><td><font color="#00FFFF">Holy Damage Taken</font></td><td>${mobHolyDmgTaken.toLocaleString()}</td></tr> <tr><td><font color="#FFFF00">Arcane Damage Taken</font></td><td>${mobArcnDmgTaken.toLocaleString()}</td></tr> <tr><td><font color="#7e01bc">Shadow Damage Taken</font></td><td>${mobShadDmgTaken.toLocaleString()}</td></tr> <tr><td><font color="#FF0000">Fire Damage Taken</font></td><td>${mobFireDmgTaken.toLocaleString()}</td></tr> <tr><td><font color="#00FF00">Kinetic Damage Taken</font></td><td>${mobKinkDmgTaken.toLocaleString()}</td></tr> <tr><td><font color="#f441be">Chaos Damage Taken</font></td><td>${mobChosDmgTaken.toLocaleString()}</td></tr> <tr><td>Hit Points Remaining</td><td>${mobRemainingHp.toLocaleString()}</td></tr> </table> </div> <a href="javascript:void(0);" id="atkInfoClose">close</a> ` // Add div document.querySelector("#container").setAttribute('style','opacity:0.1;') let newDiv = document.createElement("div"); newDiv.id = "atkInfoDiv"; newDiv.innerHTML = content document.body.appendChild(newDiv); // Remove div link document.querySelector("#atkInfoClose").addEventListener('click', async function(){ document.querySelector("#container").setAttribute('style','opacity:1;'); document.querySelector("#atkInfoDiv").remove(); }); }; async function primegods(profileData){ GM_addStyle(` .prime-img{height:40px;width:40px;border:3px SOLID #000000;} `) if (!document.URL.match(/mobid=[0-9]+/i)){ const now = Math.floor(Date.now() / 1000); const yourcrewid = profileData.crewid; const spansAlive = document.querySelectorAll('span.mobbox:not(.grey)') const spansDead = document.querySelectorAll('span.grey') const recentKills = document.querySelector(".table.table-bordered.table-striped").innerHTML; document.querySelector("#content").innerHTML = ` <div class="row justify-content-center" id="content-header-row"> <div class="bio col-lg-8 col-md-8 col-sm-12 col-12 layout-spacing layout-spacing"> <div class="widget-content widget-content-area text-left mb-3"> <h4>SPAWNED</h4> <div id="spawnedDiv"><img src="https://studiomoxxi.com/moxximod/loading-gif.gif" style="width:100px;width:100px;margin:1rem;"></div> </div> <div class="widget-content widget-content-area text-left mb-3"> <h4>RECENT LOOT</h4> <div id="recentLootDiv"><img src="https://studiomoxxi.com/moxximod/loading-gif.gif" style="width:100px;width:100px;margin:1rem;"></div> </div> </div> <div class="col-lg-4 col-md-4 col-sm-12 col-12 layout-spacing layout-spacing"> <div class="widget-content widget-content-area text-left mb-3"> <h4>DESPAWNED</h4> <div id="despawnedDiv"><img src="https://studiomoxxi.com/moxximod/loading-gif.gif" style="width:100px;width:100px;margin:1rem;"></div> </div> </div> </div> ` // Alive gods function async function getAliveGods(){ const aliveArray = []; const aliveGods = async (god) => { const name = god.innerHTML.match(/event,'([^']*)'/i)[1]; const id = god.innerHTML.match(/mobid=([0-9]+)/i)[1]; const img = god.innerHTML.match(/src="([^"]*)"/i)[1]; const page = await superfetch(`primegods?mobid=${id}`); const despawntime = page.match(/countdown = ([0-9]+)/i)[1]; const remaining = Math.floor((despawntime-now)/60); const crews = page.replace(/[\n\r]/g,'').match(/<a href="crew_profile\?id=[0-9]+">.*?<\/a><\/div><div class="grid-item" style="background-color:#[A-Za-z0-9]+; padding-right:10px; text-align:right;"><div><b>.*?<\/b>/g) || []; let totalkills = 0; let yourkills = 0; if (crews.length > 0){ for (let i = 0; i < crews.length; i++) { const c = crews[i] const crewid = c.match(/crew_profile\?id=([0-9]+)/i)[1]; const kills = parseInt(c.match(/<b>([0-9]+)<\/b>/i)[1]); totalkills += kills; if (crewid == yourcrewid){ yourkills += kills; } }; }; aliveArray.push(` <tr> <td><img src="${img}" class="prime-img"></td> <td><a href="primegods?mobid=${id}">${name}</a></td> <td>${remaining} min</td> <td>${yourkills}</td> <td>${totalkills}</td> </tr> `); }; await Promise.all(Array.from(spansAlive).map(aliveGods)); document.querySelector("#spawnedDiv").innerHTML = ` <table class="table sortable"> <thead><th></th><th>prime god</th><th>time remaining</th><th>your kills</th><th>total kills</th></thead> <tbody> ${aliveArray.join('')} </tbody> </table> ` } // Dead gods function async function getDeadGods(){ const deadArray = []; const deadGods = async (god) => { const name = god.innerHTML.match(/event,'([^']*)'/i)[1]; const id = god.innerHTML.match(/mobid=([0-9]+)/i)[1]; const img = god.innerHTML.match(/src="([^"]*)"/i)[1]; const page = await superfetch(`primegods?mobid=${id}`); const chance = page.match(/<u>Spawn Chance:<\/u>(.*?)<\/div>/i)[1]; deadArray.push(`<tr><td><a href="primegods?mobid=${id}">${name}</a></td><td>${chance}</td></tr>`); }; await Promise.all(Array.from(spansDead).map(deadGods)); document.querySelector("#despawnedDiv").innerHTML = ` <table class="table sortable"> <thead><th>prime god</th><th>spawn chance</th></thead> <tbody> ${deadArray.join('')} </tbody> </table> ` }; // Recent killed function async function recentKilledGods(){ const lootData = {} const spawnIds = recentKills.match(/primegod_loot\?spawnid=[0-9]+/g).map(i => i.match(/[0-9]+/i)[0]); const getRecentLoot = async (id) => { const spawnPage = await superfetch(`ajax/timedgod_loot_sse.php?spawnid=${id}`); const allLoot = spawnPage.match(/\{"messageType":"loottogive","data":"\[[^\]]*\]"\}/i); const itemIndex = allLoot.toString().match(/"name\\":\\"[^"]*"/g) .map(i => i .replace(/"name\\":\\"/i,'') .replace(/\\"/i,'') ); const itemRolls = spawnPage.match(/"crewid":"[0-9]+","item_index":"[0-9]+"/g) || []; for (let i = 0; i < itemRolls.length; i++) { const roll = itemRolls[i]; const item = itemIndex[i] const crewid = roll.match(/"crewid":"([0-9]+)"/i)[1]; if (!lootData[crewid]){ lootData[crewid] = [item]; } else { lootData[crewid].push(item); } }; }; await Promise.all(spawnIds.map(getRecentLoot)); const print = []; const getRecentLootForEachCrew = Object.keys(lootData).map(async key => { const crew = await superfetch(`crew_profile?id=${key}`); const picMatch = crew.match(/https:\/\/upload\.outwar\.com\/crewuploaded\/[A-Za-z][0-9]+\.[A-Za-z]+/i); const pic = picMatch ? picMatch[0] : "images/logodefault.gif"; const name = crew.match(/<h2>(.*?)<\/h2>/i)[1]; print.push(` <tr> <td><img src="${pic}" class="prime-img"></td> <td>${name}</td> <td>${lootData[key].join(', ')}</td> </tr> `); }); await Promise.all(getRecentLootForEachCrew); document.querySelector("#recentLootDiv").innerHTML = ` <table class="table sortable"> <thead><tr><th></th><th>crew</th><th>loot</th></tr></thead> <tbody>${print.join('')}</tbody> </table> ` }; // Core load functions for the page async function primeGodsPageFunctions() { await getAliveGods(); await getDeadGods(); await recentKilledGods(); sortableTables(); }; primeGodsPageFunctions(); } else { return; }; }; // Raid results async function raidResults(){ // Styling GM_addStyle(` #moxxiResults > thead > tr > th:nth-child(1){display:none;} #moxxiResults > tbody > tr > td:nth-child(1){display:none;} `) // Add moxxi raid results button const crewId = document.body.innerHTML.match(/name="crewid" value="([0-9]+)"/i)[1] document.querySelector("#content-header-row > table > tbody > tr > td > form").outerHTML = ` <center> <a href="crew_raidresults.php?crewid=${crewId}&moxximod"><button class="btn-mm">Moxximod raid results</button></a>  <form method="get" style="display:inline-block"> <input type="submit" class="btn-mm" name="all_results" value="Display all raid results">  <input type="submit" class="btn-mm" name="wins" value="Display only victorious raids">  <input type="submit" class="btn-mm" name="most_recent" value="Display most recent raids"> <input type="hidden" name="crewid" value="${crewId}"> </form> </center><hr> ` if (url.match(/crew_raidresults\.php\?crewid=[0-9]+&moxximod/i)){ // Hide header and hide existing raid table document.querySelector("#content-header-row > table > tbody > tr > td > div").setAttribute('style','display:none;'); const headerDiv = document.querySelector("#content-header-row > div.col-12.layout-spacing") if (headerDiv){ document.querySelector("#content-header-row > div.col-12.layout-spacing").setAttribute('style','display:none;') } // Add new table const oldTable = document.querySelector("#content-header-row > table"); const newElement = document.createElement("div"); newElement.classList.add('widget') newElement.innerHTML = ` <div style="width:100%;display:flex;justify-content:space-between;width:100%;margin-bottom:10px;"> <div id="showHideLosingRaids"></div> <div></div> </div> <table id="moxxiResults" class="table table-striped sortable"> <thead><tr><th>Position</th><th>Time</th><th>Raid</th><th>Chars</th><th>Result</th><th>%</th><th>Damage</th><th>Reg Block</th><th>Ele Block</th><th>Shield</th><th>Loyalty</th><th>Sin</th><th>Loot</th></tr></thead> <tbody></tbody> </table> `; oldTable.insertAdjacentElement("afterend", newElement); // Set value of stored show or hide losing raid results const hideLosingRaids = GM_getValue('hideLosingRaids') || false;; // Set the text of the show/hide losing raid results link if (hideLosingRaids){ document.querySelector("#showHideLosingRaids").innerHTML = '<a href="javascript:void();">Show losing raids</a>' } else { document.querySelector("#showHideLosingRaids").innerHTML = '<a href="javascript:void();">Hide losing raids</a>' }; // Show/hide losing raid results link document.querySelector("#showHideLosingRaids").addEventListener('click', async function(){ if (hideLosingRaids){ GM_setValue('hideLosingRaids',false); window.location = window.location; } else { GM_setValue('hideLosingRaids',true); window.location = window.location; }; }); // Get array of all recent raids and pull data let data = []; const raidsArray = document.querySelectorAll("#content-header-row > table > tbody > tr > td > div > center > div > table > tbody > tr"); const buildTable = async (item,index) => { const row = item.innerHTML; const raidPosition = index + 1 const raidTime = row.match(/[0-9]+:[0-9]+/i); const raidAttackers = parseInt(row.match(/<td align="left" valign="top">([0-9]+)<\/td>/i)[1]); const raidResult = row.match(/<font color="[^"]*"><b>.*?<\/b><\/font>/i); if (hideLosingRaids && raidResult.toString().match('LOSS')){ return; }; const raidId = row.match(/raidid=([0-9]+)/i)[1]; const raidName = row.match(/>.*?</g)[1].toString().match(/>(.*?)</i)[1].replace(/(,.*|of.*| The | the.*)/g, ''); const raidFetch = await superfetch(`raidattack.php?raidid=${raidId}`); const raidPercent = raidFetch.match(/[0-9]+%<\/span><\/div>/g).map(i => i.match(/[0-9]+/)).slice(-1).toString(); const raidDamage = parseInt(raidFetch.replace(/,/g, '').match(/<i>Total Attacker Damage: ([0-9]+)<\/i>/i)[1]); const raidAttacks = raidFetch.match(/Base: [0-9]+/g).length; const raidEleblock = raidFetch.match(/images\/block2\.jpg/g) ? Math.ceil((raidFetch.match(/images\/block2\.jpg/g).length)/raidAttacks*100) : 0; const raidRegblock = raidFetch.match(/images\/block\.jpg/g) ? Math.ceil((raidFetch.match(/images\/block\.jpg/g).length)/raidAttacks*100) : 0; const raidDied = raidFetch.match(/images\/dead\.jpg/g) ? (raidFetch.match(/images\/dead\.jpg/g).length) : 0; const raidShield = raidFetch.match(/items\/[A-Za-z]+_ele_shield\.jpg/g) ? Math.ceil((raidFetch.match(/items\/[A-Za-z]+_ele_shield\.jpg/g).length) / raidAttacks * 100) : 0; const raidItems = (raidFetch.match(/<a href="#" onmouseover="popup\(event,'<b>(.*?)<\/b>'\)" onmouseout="kill\(\)">.*?<\/a>/i) || ['',''])[1]; const alvarLoyalty = raidFetch.match(/Alvar Loyalty/i) ? raidFetch.replace(/[\n\r]/g,'').replace(/\s+/g,'').match(/AlvarLoyalty\/MaximumLoyalty'\);"onmouseout="kill\(\);">[0-9]+\/[0-9]+/i) : "0/0"; const delrukLoyalty = raidFetch.match(/Delruk Loyalty/i) ? raidFetch.replace(/[\n\r]/g,'').replace(/\s+/g,'').match(/DelrukLoyalty\/MaximumLoyalty'\);"onmouseout="kill\(\);">[0-9]+\/[0-9]+/i) : "0/0"; const vordynLoyalty = raidFetch.match(/Vordyn Loyalty/i) ? raidFetch.replace(/[\n\r]/g,'').replace(/\s+/g,'').match(/VordynLoyalty\/MaximumLoyalty'\);"onmouseout="kill\(\);">[0-9]+\/[0-9]+/i) : "0/0"; const raidLoyalty = parseInt(alvarLoyalty.toString().match(/([0-9]+)\/[0-9]+/i)[1])+parseInt(delrukLoyalty.toString().match(/([0-9]+)\/[0-9]+/i)[1])+parseInt(vordynLoyalty.toString().match(/([0-9]+)\/[0-9]+/i)[1]) const raidMaxloyalty = parseInt(alvarLoyalty.toString().match(/[0-9]+\/([0-9]+)/i)[1])+parseInt(delrukLoyalty.toString().match(/[0-9]+\/([0-9]+)/i)[1])+parseInt(vordynLoyalty.toString().match(/[0-9]+\/([0-9]+)/i)[1]) const raidSin = (raidFetch.match(/color:#CC0000;"><b>(.*?)<\/b>/i) || [])[1] === "0" ? "YES" : "NO"; data.push(rowData = { position: raidPosition, time: raidTime, name: `<a href="raidattack.php?raidid=${raidId}">${raidName}</a>`, attackers: `${raidAttackers} (🕱${raidDied})`, result: raidResult, percent: `${raidPercent}%`, damage: raidDamage.toLocaleString(), regblock: `${raidRegblock}%`, eleblock: `${raidEleblock}%`, shield: `${raidShield}%`, loyalty: `${raidLoyalty}/${raidMaxloyalty}`, sin: raidSin, items: raidItems }); }; await Promise.all(Array.from(raidsArray).slice(1).map((raid, index) => buildTable(raid, index))); // Sort the data object and add to table as new rows data.sort((a, b) => a.position - b.position); data.forEach(rowData => { const rowElement = document.querySelector("#moxxiResults > tbody").insertRow(); rowElement.innerHTML = Object.values(rowData).map(value => `<td>${value}</td>`).join(''); }); // Make table sortable await sortableTables(); }; }; // Change faction rebuild async function changeFaction(profileData){ GM_addStyle(` .codex-img { width: calc(10% - 10px); margin: 5px; box-shadow: 0 0 3px rgba(0, 0, 0, 0.5); } `) // Remove change faction heading document.querySelector("#content-header-row > div > h1").remove(); // Update right-hand content const rightContent = document.querySelector("#content-header-row > div > div > div:nth-child(2)"); const completedCodex = document.querySelector("#content-header-row > div > div > div > div.mt-3 > div > div.widget-content.widget-content-area").innerHTML; const completedArray = completedCodex.match(/Triworld Codex Chapter [0-9]+/g) || []; const imgArray = []; for (let i = 0; i < completedArray.length; i++) { imgArray.push(`<img src="images/items/tiupgrade${completedArray[i].match(/[0-9]+/i)}.png" class="codex-img">`); }; rightContent.outerHTML = ` <div class="col-12 col-md-8"> <div class="widget mb-3" id="codexContainer"><h3>COMPLETED CODEX</h3><span style="display:inline-block;text-align:left;">${imgArray.join('')}</span></div> <div class="widget mb-3"><h3>LOYALTY RANKINGS</h3> <div id="loyaltyRankings"><img src="https://studiomoxxi.com/moxximod/loading-gif.gif" height="90px" width="90px"></div> </div> <div class="widget"> <h3>FACTION LOYALTY</h3><p> Players can increase their loyalty with each faction (up to a maximum of 10) by completing Catalyst tasks provided by the Alvar Emissary, Delruk Emissary and Vordyn Emissary. Completing these tasks will award a Proven Loyalty item, which can be exchanged with Artel, Dumar or Valka to permanently increase your loyalty with that faction. Secondary rewards such as augments are also earned by increasing loyalty. The first 2 catalyst quests have been added with this update, with the remaining 8 to be released with future updates. While the Catalyst of Evolution is playerbound, all other catalysts will be tradeable.<p> Increasing your loyalty with each faction can provide bonus damage for yourself vs mobs and for all members vs raids. The bonus damage amount is determined by the effect % each encounter has, multiplied by the amount of maximum loyalty that takes effect during the encounter. Loyalty will only take effect through your current active faction.<p> In addition to bonus damage, loyalty can also be used to increase the power of your Triworld Influence skill, which is earned by completing the Faction Initiations quest. The power of this skill is determined by the amount of codexes collected, increased by up to 100% based on your highest faction loyalty (10% per loyalty). Codexes can be found from expansion related content which players need to discover, with 29 being added with this initial update out of the eventual total of 50. While codex 1-10 are playerbound, all other codexes will be tradeable.<p> </div> </div> ` // Update left-hand content const leftContent = document.querySelector("#content-header-row > div > div > div:nth-child(1)") leftContent.outerHTML = ` <div class="col-12 col-md-4"> <div class="widget mb-3" id="factionLogo">test</div> <div class="widget mb-3"><h3>CHANGE FACTION</h3>${leftContent.innerHTML}</div> <div class="widget"> <h3>FACTIONS</h3><p> <font color="#f6b861">The <b>Delruk</b> Alliance was formed by members of Diamond City and other nearby lands. They focus on base attack and vile damage.</font><p> <font color="#b7d9fb">The <b>Alvar</b> Liberation was formed by the survivors of the Astral Dimension war. They focus on original elemental damages.</font><p> <font color="#ffb7f1">The <b>Vordyn</b> Rebellion was formed on Veldara during the reign of Thanox. They focus on chaos damage.</font><p> Factions work similar to classes, with each character being able to choose one of 3 options. Each character is able to change faction once per month for free via the Change Faction page (also linked via the character menu) and the free faction change timer resets at the start of every month, however changing faction more than once per month will require a Faction Change item. Unlike classes, bonuses for being in each faction are not initially provided via direct stats, instead they provide access to faction specific content and activate your Faction Loyalty. While this initial update focuses primarily on the loyalty system and damage bonuses, content including level 95 enhancements will be added in the future focusing on faction specific stats.<p> </div> </div> ` // Get current faction const faction = profileData.faction; if (faction == "Vordyn"){ document.querySelector("#factionLogo").innerHTML = `<img src="https://studiomoxxi.com/moxxibots/factions/v.png" style="height:200px;width:200px;margin:20px;">` GM_addStyle (`.widget,.widget-content-area{-webkit-box-shadow:0px 0px 3px 3px rgba(252,41,205,0.3);`) } else if (faction == "Alvar"){ document.querySelector("#factionLogo").innerHTML = `<img src="https://studiomoxxi.com/moxxibots/factions/a.png" style="height:200px;width:200px;margin:20px;">` GM_addStyle (`.widget,.widget-content-area{-webkit-box-shadow:0px 0px 3px 3px rgba(0,159,255,0.3);`) } else if (faction == "Delruk"){ document.querySelector("#factionLogo").innerHTML = `<img src="https://studiomoxxi.com/moxxibots/factions/d.png" style="height:200px;width:200px;margin:20px;">` GM_addStyle (`.widget,.widget-content-area{-webkit-box-shadow:0px 0px 3px 3px rgba(255,120,39,0.3);`) } else { document.querySelector("#factionLogo").innerHTML = "No faction selected" } document.querySelector("#content-header-row > div > div > div:nth-child(1) > div.widget.mb-3 > h4").outerHTML = "Free faction change every calendar month" // Remove native elements document.querySelector("#content-header-row > div > div > div:nth-child(1) > div.widget.mb-3 > div.mt-3").remove(); document.querySelector(".clock-builder-output.flip-clock-small-wrapper").remove(); // Add margin to the bottom of the change faction button document.querySelector("#content-header-row > div > div > div:nth-child(1) > div.widget.mb-3 > form > button").setAttribute('style','margin-bottom:1rem;') // Call function for rankings factionRankings(); }; async function factionRankings(){ // Styling GM_addStyle(` table.alvar-rankings > tbody > tr > th {color:#b7d9fb;} table.delruk-rankings > tbody > tr > th {color:#f6b861;} table.vordyn-rankings > tbody > tr > th {color:#ffb7f1;} `) // Fetch loyalty rankings const powerRankings = await superfetch('ajax/rankings.php?type=char_power'); const idArray = powerRankings.match(/"id":"[0-9]+"/g).map(i => i.match(/[0-9]+/)); const rankings = { alvar: [], delruk: [], vordyn: [] }; const profiles = async (i) => { const profile = await superfetchProfile(`profile?id=${i}`); const name = profile.name; const faction = profile.faction; const loyalty = profile.loyalty; if (faction != "None"){ rankings[faction.toLowerCase()].push({name:name,level:loyalty}); }; } await Promise.all(idArray.map(profiles)); // Sort by loyalty level for (var key in rankings) { if (rankings.hasOwnProperty(key)) { // Sort the array of objects by the "level" property rankings[key].sort((a, b) => b.level - a.level); rankings[key] = rankings[key].slice(0, 15); }; }; var alvarTable = '<table class="rankings-table table table-striped alvar-rankings"><tr><th><img src="https://studiomoxxi.com/moxxibots/factions/a.png" height="15px" width="15px"></th><th>ALVAR</th><th>LVL</th></tr>' + rankings.alvar.map((obj, index) => `<tr><td>${index + 1}</td><td width="180px">${obj.name}</td><td>${obj.level}</td></tr>`).join('') + '</table>'; var delrukTable = '<table class="rankings-table table table-striped delruk-rankings"><tr><th><img src="https://studiomoxxi.com/moxxibots/factions/d.png" height="15px" width="15px"></th><th>DELRUK</th><th>LVL</th></tr>' + rankings.delruk.map((obj, index) => `<tr><td>${index + 1}</td><td width="180px">${obj.name}</td><td>${obj.level}</td></tr>`).join('') + '</table>'; var vordynTable = '<table class="rankings-table table table-striped vordyn-rankings"><tr><th><img src="https://studiomoxxi.com/moxxibots/factions/v.png" height="15px" width="15px"></th><th>VORDYN</th><th>LVL</th></tr>' + rankings.vordyn.map((obj, index) => `<tr><td>${index + 1}</td><td width="180px">${obj.name}</td><td>${obj.level}</td></tr>`).join('') + '</table>'; document.querySelector("#loyaltyRankings").innerHTML = ` <div style="display:inline-block"> ${alvarTable}</div> <div style="display:inline-block"> ${delrukTable}</div> <div style="display:inline-block"> ${vordynTable}</div> <br><i>Loyalty rankings only include players who rank top-100 for power. Other players may have a higher loyalty level but will not appear on loyalty rankings ` }; // Crew profile rebuild async function crewProfile(server){ // Styling GM_addStyle (` .crew-profile-pic{box-shadow: 5px 5px 5px rgba(0, 0, 0, 1);width:100%;border-radius:6px;display:inline-block;} #nativeMemberTable{max-height:533px;overflow:auto;} #crewMemberDiv{width:100%;text-align:left;display:none;margin-top:15px;} #crewMemberTable > tbody > tr > td > img{width:30px;height:30px;display:inline-block;border-radius:10px;} .button-container{display:inline-block;} #scrollButtons > center > a{padding:10px;font-size:16px;} #scrollButtons{padding-left:10px;padding-bottom:10px;padding-right:10px;} #actionSpace > img{margin:4px;padding:4px;box-shadow: 5px 5px 5px rgba(0, 0, 0, 1);width:40px;height:40px;border: 2px inset;} #actionSpace{overflow-y:auto;overflow-x:hidden;max-height:250px;} #crewMemberTable{height:100%;overflow:hidden;box-sizing: border-box;} `); // Build variables const crewName = document.body.innerHTML.match(/<h2>(.*?)<\/h2>/i)[1] const crewPic = (document.body.innerHTML.match(/<img src="([^"]*)" width="[0-9]+" height="[0-9]+">/i) || ['','images/logodefault.gif'])[1] const crewUpgrades = document.body.innerHTML.match(/<img border="0" src="[^"]*" onmouseover="popup\(event,'<div style="width:200px"><b>.*? \(Level [0-9]+\/[0-9]+ \+[0-9]+\)<\/b><br>.*?<\/div>','808080'\)" ;="" onmouseout="kill\(\)">/g) const crewStones = document.body.innerHTML.match(/<img style="border:2px inset;" src="[^"]*" onmouseover="[^"]*" ;="" onmouseout="kill\(\)">/g) || []; const crewAllies = document.body.innerHTML.replace(/[\n\r]/g,'').replace(/col-6/g,'col-4').match(/(<h5 class="card-title">CREW ALLIES<\/h5>.*?)<h5 class="card-title">CREW ENEMIES<\/h5>/i)[1]; const crewMembers = document.body.innerHTML.match(/<td><a href="profile\.php\?id=[0-9]+">.*?<\/a><\/td>/g); const parseData = new DOMParser(); const bodyHtml = parseData.parseFromString(document.body.innerHTML, 'text/html'); const nativeMemberTable = bodyHtml.querySelector('.table.table-bordered.table-striped').innerHTML; const nativeDetails = bodyHtml.querySelector('.row div ul').parentNode.parentNode.outerHTML; const breakAlliance = document.body.innerHTML.match(/id=[0-9]+&ally=2/i) const formAlliance = document.body.innerHTML.match(/id=[0-9]+&ally=1/i) const dropdownOptions = bodyHtml.querySelector("#content-header-row > div:nth-child(2) > div > div.btn-group.mb-3.mr-2"); const treasuryLink = document.body.innerHTML.match(/<a href="(\/treasury\?search_for=.*?)">/i)[1] const crewId = document.body.innerHTML.match(/<a href="crew_raidresults\.php\?crewid=([0-9]+)&most_recent=1">/i)[1] // Static crew profile buttons const myCrewButtons = ` <div class="btn-group show" role="group"> <button type="button" class="btn-mm dropdown-toggle" data-toggle="dropdown">CREW PAGES <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-down"><polyline points="6 9 12 15 18 9"></polyline></svg></button> <div id="crewActionsDropdown" class="dropdown-menu" style="will-change: transform; position: absolute; transform: translate3d(0px, 38px, 0px); top: 0px; left: 0px;"> <a class="dropdown-item" href="trade?isCrewTrade=1&tradeWith=${crewId}">Trade with ${crewName}</a> <a class="dropdown-item" href="${treasuryLink}">${crewName} Treasury</a> <a class="dropdown-item" href="crew_raidresults.php?crewid=${crewId}&moxximod">Raid Results</a> <a class="dropdown-item" href="javascript:void(0);" id="showCrewUpgrades">Crew Upgrades</a> <a class="dropdown-item" href="javascript:void(0);" id="showCrewAllies">Crew Allies</a> </div> </div>` // Build HTML content document.querySelector("#content").innerHTML = ` <div class="widget-content widget-content-area"> <div class="bio-skill-box"> <div class="row"> <div class="col-12 col-xl-5"> <h3>${crewName}</h3><hr> ${nativeDetails}<hr> <div> <div class="button-container"> <div class="btn-group show" role="group"> <button type="button" class="btn-mm dropdown-toggle" data-toggle="dropdown">MEMBER DETAILS <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-down"><polyline points="6 9 12 15 18 9"></polyline></svg></button> <div class="dropdown-menu" style="will-change: transform; position: absolute; transform: translate3d(0px, 38px, 0px); top: 0px; left: 0px;"> <a class="dropdown-item" href="javascript:void(0);" id="mbr-stats">STATISTICS</a> <a class="dropdown-item" href="javascript:void(0);" id="mbr-items">ITEMS</a> <a class="dropdown-item" href="javascript:void(0);" id="mbr-skills">SKILLS</a> </div> </div> </div> <div class="button-container"> ${myCrewButtons} </div> <hr> <div id="actionSpace"></div> </div> </div> <div class="col-12 col-xl-4"> <img src="${crewPic}" class="crew-profile-pic"> </div> <div class="col-12 col-xl-3"> <div id="nativeMemberTable"> <table class="table table-striped"> ${nativeMemberTable.replace(/<th>Level<\/th>/i,'').replace(/<td>\b\d{1,3}\b<\/td>/g,'')} </table> </div> </div> </div> </div> </div> <div id="crewMemberDiv" class="widget-content widget-content-area" style="border-radius:0px;top:-5px;"> <div id="crewMemberTable"></div> </div> ` // Dynamic crew profile buttons var crewActionsDropdown = document.getElementById("crewActionsDropdown"); if (dropdownOptions){ if (dropdownOptions.innerHTML.match(/href="\/crew_invites"/)) { crewActionsDropdown.innerHTML += `<a class="dropdown-item" href="crew_invites">Send Crew Invites</a>`; } if (dropdownOptions.innerHTML.match(/href="\/crew_image"/)) { crewActionsDropdown.innerHTML += `<a class="dropdown-item" href="crew_image">Edit Crew Picture</a>`; } if (dropdownOptions.innerHTML.match(/href="\/crew_changerank"/)) { crewActionsDropdown.innerHTML += `<a class="dropdown-item" href="crew_changerank">Change Ranks</a>`; } if (dropdownOptions.innerHTML.match(/href="\/crew_ranknames"/)) { crewActionsDropdown.innerHTML += `<a class="dropdown-item" href="crew_ranknames">Change Rank Names</a>`; } if (dropdownOptions.innerHTML.match(/href="\/crew_bootmem"/)) { crewActionsDropdown.innerHTML += `<a class="dropdown-item" href="crew_bootmem">Boot Members</a>`; } if (dropdownOptions.innerHTML.match(/href="\/crew_actionlog"/)) { crewActionsDropdown.innerHTML += `<a class="dropdown-item" href="crew_actionlog">Crew Action Log</a>`; } if (dropdownOptions.innerHTML.match(/href="\/crew_vault"/)) { crewActionsDropdown.innerHTML += `<a class="dropdown-item" href="crew_vault">Vault & Storage</a>`; } if (dropdownOptions.innerHTML.match(/href="\/crew_capstatus"/)) { crewActionsDropdown.innerHTML += `<a class="dropdown-item" href="crew_capstatus">Character Cap Status</a>`; } if (dropdownOptions.innerHTML.match(/href="\/crew_adminpanel"/)) { crewActionsDropdown.innerHTML += `<a class="dropdown-item" href="crew_adminpanel">Admin Panel</a>`; } }; // Form alliance and break alliance links if (formAlliance){ var a = document.createElement("a"); a.className = "dropdown-item"; a.setAttribute('style','cursor:pointer;') a.addEventListener('click', function(){ window.location.href = `crew_profile.php?${formAlliance.toString().replace('amp;','')}` alert('You are now allied to this crew'); window.location.href = window.location.href.replace(/&ally=[0-9]+/i,''); }) a.textContent = "Form Alliance"; crewActionsDropdown.appendChild(a); }; if (breakAlliance){ var a = document.createElement("a"); a.className = "dropdown-item"; a.setAttribute('style','cursor:pointer;') a.addEventListener('click', function(){ window.location.href = `crew_profile.php?${breakAlliance.toString().replace('amp;','')}` alert('You are no longer allied to this crew'); window.location.href = window.location.href.replace(/&ally=[0-9]+/i,''); }) a.textContent = "Break Alliance"; crewActionsDropdown.appendChild(a); }; // Add click functions to member information dropdown buttons document.querySelector("#mbr-stats").addEventListener('click',memberStats) document.querySelector("#mbr-items").addEventListener('click',memberItems) document.querySelector("#mbr-skills").addEventListener('click',memberSkills) // Member stats async function memberStats(){ // Remove event listener and add loading GIF document.querySelector("#actionSpace").innerHTML = `<div><img src="https://studiomoxxi.com/moxximod/loading-gif.gif" style="height:100px;width:100px;padding:20px"></div>` // Collect member data const memberData = [] let count = 0 const buildTable = async (item) => { count++ const position = count; const charId = item.match(/<a href="profile\.php\?id=([0-9]+)">/i)[1]; const profile = (await superfetch(`profile?id=${charId}`)).replace(/\s/g,''); const name = `<a href='profile.php?id=${charId}'>${profile.match(/<fontsize="3">(.*?)<\/font>/i)[1]}</a>`; const level = profile.match(/<fontsize="2">Level([0-9]+).*?<\/font>/i)[1]; const power = parseInt(profile.replace(/,/g,'').match(/TOTALPOWER<\/font><\/b><\/td>.*?<fontsize="2">([0-9]+)<\/font>/i)[1]).toLocaleString(); const ele = parseInt(profile.replace(/,/g,'').match(/ELEMENTALATTACK<\/font><\/b><\/td>.*?<fontsize="2">([0-9]+)<\/font>/i)[1]).toLocaleString(); const slayer = parseInt(profile.replace(/,/g,'').match(/GODSLAYERLEVEL<\/font><\/b><\/td>.*?<fontsize="2">([0-9]+)<\/font>/i)[1]).toLocaleString(); const wldr = parseInt(profile.replace(/,/g,'').match(/WILDERNESSLEVEL<\/font><\/b><\/td>.*?<fontsize="2">([0-9]+)<\/font>/i)[1]).toLocaleString(); const chaos = parseInt(profile.replace(/,/g,'').match(/CHAOSDAMAGE<\/font><\/b><\/td.*?<fontsize="2">([0-9]+)<\/font>/i)[1]).toLocaleString(); const atk = parseInt(profile.replace(/,/g,'').match(/ATTACK<\/font><\/b><\/td>.*?<fontsize="2">([0-9]+)<\/font>/i)[1]).toLocaleString(); const hp = parseInt(profile.replace(/,/g,'').match(/HITPOINTS<\/font><\/b><\/td>.*?<fontsize="2">([0-9]+)<\/font>/i)[1]).toLocaleString(); const growth = parseInt(profile.replace(/,/g,'').match(/GROWTHYESTERDAY<\/font><\/b><\/td>.*?<fontsize="2">(.*?)<\/font>/i)[1]).toLocaleString(); const resistance = parseInt(profile.replace(/,/g,'').match(/ELEMENTALRESIST<\/font><\/b><\/td>.*?<fontsize="2">([0-9]+)<\/font>/i)[1]).toLocaleString(); const factionMatch = profile.match(/<fontsize="1">FACTION<\/font><\/b><\/td><tdwidth="50%"style="padding-top:2px;padding-bottom:2px;"><b><fontsize="2">(.*?)\((.*?)\)<\/font>/i) const faction = factionMatch[1] const loyalty = factionMatch[2] === "" ? '0' : factionMatch[2]; memberData.push({ position,name,level,power,ele,chaos,faction,loyalty,slayer,wldr,atk,hp,resistance,growth }); }; await Promise.all(crewMembers.map(buildTable)); // Sort by position value memberData.sort((a, b) => a.position - b.position); // Remove position value after sorting const memberDataWithoutPosition = memberData.map(({ position, ...rest }) => rest); const allRows = []; memberDataWithoutPosition.forEach(char => { const row = []; Object.keys(char).forEach(td => { row.push(`<td>${char[td]}</td>`); }); allRows.push(`<tr>${row.join('')}</tr>`); }); document.querySelector("#crewMemberTable").innerHTML = ` <table id="crewMemberTable" class="table sortable"> <thead><th>name</th><th>level</th><th>power</th><th>ele</th><th>chaos</th><th>faction</th><th>loyalty</th><th>slayer</th><th>wldr</th><th>atk</th><th>hp</th><th>resistance</th><th>growth</th></thead> <tbody>${allRows.join('')}</tbody> </table> ` // Get totals data let totalPower = 0; let totalEle = 0; let totalChaos = 0; let totalLoyalty = 0; let totalSlayer = 0; let totalWldr = 0; let totalRes = 0; let totalGrowth = 0; let totalAtk = 0; let totalHp = 0 memberDataWithoutPosition.forEach(function(obj) { Object.entries(obj).forEach(function([key, value]) { var cell = document.createElement("td"); cell.innerHTML = value; if (value.match(/<a href='profile\.php\?id=[0-9]+'>.*?<\/a>/i)){ cell.setAttribute('style',`position: sticky;left:0px;`); }; if (key == "power"){ totalPower += parseInt(value.replace(/,/g,'')) }; if (key == "ele"){ totalEle += parseInt(value.replace(/,/g,'')) }; if (key == "chaos"){ totalChaos += parseInt(value.replace(/,/g,'')) }; if (key == "loyalty"){ totalLoyalty += parseInt(value.replace(/,/g,'')) }; if (key == "wldr"){ totalWldr += parseInt(value.replace(/,/g,'')) }; if (key == "slayer"){ totalSlayer += parseInt(value.replace(/,/g,'')) }; if (key == "resistance"){ totalRes += parseInt(value.replace(/,/g,'')) }; if (key == "growth"){ totalGrowth += parseInt(value.replace(/,/g,'')) }; if (key == "atk"){ totalAtk += parseInt(value.replace(/,/g,'')) }; if (key == "hp"){ totalHp += parseInt(value.replace(/,/g,'')) }; }); }); // Unhide table GM_addStyle(`#crewMemberDiv{overflow:hidden;display:block;}`); // Remove loading GIF once elements load document.querySelector("#actionSpace").innerHTML = ` <div class="row"> <div class="col-6 pr-1"> <ul class="list-group text-left"> <li class="list-group-item"><b>Total Power:</b> ${totalPower.toLocaleString()}</li> <li class="list-group-item"><b>Total Elemental:</b> ${totalEle.toLocaleString()}</li> <li class="list-group-item"><b>Total Resistance:</b> ${totalRes.toLocaleString()}</li> <li class="list-group-item"><b>Total God Slayer:</b> ${totalSlayer.toLocaleString()}</li> <li class="list-group-item"><b>Total Growth:</b> ${totalGrowth.toLocaleString()}</li> </ul> </div> <div class="col-6 pl-1"> <ul class="list-group text-left"> <li class="list-group-item"><b>Total Chaos Damage:</b> ${totalChaos.toLocaleString()}</li> <li class="list-group-item"><b>Total Faction Loyalty:</b> ${totalLoyalty.toLocaleString()}</li> <li class="list-group-item"><b>Total Wilderness:</b> ${totalWldr.toLocaleString()}</li> <li class="list-group-item"><b>Total Attack:</b> ${totalAtk.toLocaleString()}</li> <li class="list-group-item"><b>Total Hit Points:</b> ${totalHp.toLocaleString()}</li> </ul> </div> </div> ` // Run function to make member table sortable await sortableTables(); }; async function memberItems(){ // Remove event listener and add loading GIF document.querySelector("#actionSpace").innerHTML = `<div><img src="https://studiomoxxi.com/moxximod/loading-gif.gif" style="height:100px;width:100px;padding:20px"></div>` // Collect member data const memberData = [] let count = 0 const buildTable = async (item) => { count++ const position = count; const charId = item.match(/<a href="profile\.php\?id=([0-9]+)">/i)[1]; const profile = (await superfetch(`profile?id=${charId}`)).replace(/[\n\r]/g,''); const name = `<a href='profile.php?id=${charId}'>${profile.match(/<font size="3">(.*?)<\/font>/i)[1]}</a>`; const core = profile.match(/left:61px; top:12px; width:41px; height:41px;text-align:center">(.*?)<\/div>/i)[1] const head = profile.match(/left:118px; top:7px; width:62px; height:46px;text-align:center">(.*?)<\/div>/i)[1] const neck = profile.match(/left:197px; top:12px; width:41px; height:41px;text-align:center">(.*?)<\/div>/i)[1] const wepn = profile.match(/left:45px; top:67px; width:56px; height:96px;text-align:center">(.*?)<\/div>/i)[1] const body = profile.match(/left:121px; top:67px; width:56px; height:96px;text-align:center">(.*?)<\/div>/i)[1] const shld = profile.match(/left:198px; top:67px; width:56px; height:96px;text-align:center">(.*?)<\/div>/i)[1] const belt = profile.match(/left:61px; top:192px; width:41px; height:41px;text-align:center">(.*?)<\/div>/i)[1] const pant = profile.match(/left:118px; top:175px; width:62px; height:75px;text-align:center">(.*?)<\/div>/i)[1] const ring = profile.match(/left:197px; top:192px; width:41px; height:41px;text-align:center">(.*?)<\/div>/i)[1] const foot = profile.match(/left:118px; top:262px; width:62px; height:66px;text-align:center">(.*?)<\/div>/i)[1] const cgem = profile.match(/left:10px; top:346px; width:32px; height:32px;text-align:center">(.*?)<\/div>/i)[1] const bdge = profile.match(/left:214px; top:346px; width:32px; height:32px;text-align:center">(.*?)<\/div>/i)[1] const rune = profile.match(/left:54px; top:346px; width:32px; height:32px;text-align:center">(.*?)<\/div>/i)[1] const bstr = profile.match(/left:258px; top:346px; width:32px; height:32px;text-align:center">(.*?)<\/div>/i)[1] const orbs = profile.match(/left:100px; top:346px; width:99px; height:32px;text-align:center">(.*?)<\/div>/i)[1] memberData.push({ position,name,core,head,neck,wepn,body,shld,belt,pant,ring,foot,orbs,cgem,bdge,rune,bstr }); }; await Promise.all(crewMembers.map(buildTable)); // Sort by position value memberData.sort((a, b) => a.position - b.position); // Remove position value const memberDataWithoutPosition = memberData.map(({ position, ...rest }) => rest); const allRows = []; memberDataWithoutPosition.forEach(char => { const row = []; Object.keys(char).forEach(td => { row.push(`<td>${char[td]}</td>`); }); allRows.push(`<tr>${row.join('')}</tr>`); }); document.querySelector("#crewMemberTable").innerHTML = ` <table id="crewMemberTable" class="table"> <tbody>${allRows.join('')}</tbody> </table> ` // Unhide table GM_addStyle(`#crewMemberDiv{overflow:hidden;display:block;}`); // Remove loading GIF once elements load document.querySelector("#actionSpace").innerHTML = '' }; async function memberSkills(){ // Remove event listener and add loading GIF document.querySelector("#actionSpace").innerHTML = `<div><img src="https://studiomoxxi.com/moxximod/loading-gif.gif" style="height:100px;width:100px;padding:20px"></div>` // Collect member data const memberData = [] let count = 0 const buildTable = async (item) => { count++ const position = count; const charId = item.match(/<a href="profile\.php\?id=([0-9]+)">/i)[1]; const profile = await superfetch(`profile?id=${charId}`); const parseData = new DOMParser(); const profileHtml = parseData.parseFromString(profile, 'text/html'); const skills = profileHtml.querySelector("#divSkillsCast").innerHTML.replace(/<img src=".*?ProfileSkills\.png">/i,'') const name = `<a href='profile.php?id=${charId}'>${profile.match(/<font size="3">(.*?)<\/font>/i)[1]}</a>`; memberData.push({ position,name,skills }); }; await Promise.all(crewMembers.map(buildTable)); // Sort by position value memberData.sort((a, b) => a.position - b.position); // Remove position value const memberDataWithoutPosition = memberData.map(({ position, ...rest }) => rest); const allRows = []; memberDataWithoutPosition.forEach(char => { const row = []; Object.keys(char).forEach(td => { row.push(`<td>${char[td]}</td>`); }); allRows.push(`<tr>${row.join('')}</tr>`); }); document.querySelector("#crewMemberTable").innerHTML = ` <table id="crewMemberTable" class="table"> <tbody>${allRows.join('')}</tbody> </table> ` // Unhide table GM_addStyle(`#crewMemberDiv{overflow:hidden;display:block;}`); // Remove loading GIF once elements load document.querySelector("#actionSpace").innerHTML = '' }; // Crew upgrades document.querySelector("#showCrewUpgrades").addEventListener('click',function(){ document.querySelector("#actionSpace").innerHTML = `${crewUpgrades.join(' ')}<br>${crewStones.join(' ')}` }); // Crew allies document.querySelector("#showCrewAllies").addEventListener('click',function(){ document.querySelector("#actionSpace").innerHTML = crewAllies }); // Notes notes(crewName,crewId,server); }; async function augmentEquip(profileData,server){ GM_addStyle(` .augment-equip-item-div > img {height:40px;width:40px;margin:3px;border-radius:8px;border:2px #475254 SOLID;} .selectable-item,.selectable-aug,.selectable-item-table {cursor:pointer} .btn-aug {height:40px;width:100%;margin-bottom:10px;} .btn-item {padding:10px;height:60px;width:60px;margin:3px;border-radius:16px;border:2px #475254 SOLID;background:#000000;box-shadow:5px 5px 5px rgba(0, 0, 0, 1);} #equippedAugDataTable > tbody > tr > td > img{height:30px;width:30px;border-radius:8px;border:2px #475254 SOLID;} div.item-action{display:inline-block;cursor:pointer;} div.item-action:hover{opacity:0.5;} `) // Grab equipped items const slots = ["core","head","neck","weapon","body","shield","belt","pants","ring","foot"] const equippedArray = []; const equippedItems = Object.fromEntries(Object.entries(profileData).filter(([key]) => slots.includes(key))); const equippedIds = Object.values(equippedItems).map(item => item.id); for (const item of Object.values(equippedItems)) { const id = item.id; const img = item.img; equippedArray.push(`<img src="${img}" onmouseover="itempopup(event,'${id}')" onmouseout="kill()" class="selectable-item">`); }; // Grab unequipped items const unequippedItems = document.querySelectorAll('div.divItem'); const unequippedArray = []; for (let item of unequippedItems){ const itemId = item.innerHTML.match(/event,'([0-9]+)'/i)[1]; if (!equippedIds.includes(itemId)){ item.querySelector('img').setAttribute('class','selectable-item') unequippedArray.push(item.innerHTML); }; }; // Grab all available augments const availableAugs = []; const allAugs = document.querySelectorAll('div[id^="augment"]'); for (let aug of allAugs){ const augId = aug.innerHTML.match(/event,'([0-9]+)'/i)[1]; aug.querySelector('img').setAttribute('class','selectable-aug'); aug.querySelector('img').setAttribute('id',`augment-${augId}`); availableAugs.push(aug.innerHTML.replace(/onclick="[^"]*"/i,'')); }; // Get the value of body background to set the background of the selected item div to that color const backgroundColor = window.getComputedStyle(document.body).backgroundColor; // Build HTML document.querySelector("#content").innerHTML = ` <div class="row justify-content-center"> <div class="col-lg-5 col-md-5 col-sm-12 col-12 layout-spacing layout-spacing"> <div class="widget profile-widget mb-3 augment-equip-item-div"> <h4>EQUIPPED ITEMS</h4> ${equippedArray.join('')} </div> <div class="widget profile-widget mb-3 augment-equip-item-div"> <h4>OTHER ITEMS</h4> ${unequippedArray.join('')} </div> <div class="widget profile-widget mb-3 augment-equip-item-div" id="availableAugsDiv"> <h4>AVAILABLE AUGMENTS</h4> ${availableAugs.sort().join('')} </div> </div> <div class="col-lg-7 col-md-7 col-sm-12 col-12 layout-spacing"> <div class="widget profile-widget mb-3" style="display:none;" id="showHideDiv"> <div id="slottedAugsDropdownMenu" class="mb-3"></div> <div style="display:inline-block"> <div id="augmentEquipSlottedAug" class="mb-3" style="background:${backgroundColor};display:none;margin-right:10px;text-align:left;border:2px #475254 solid;padding:10px;border-radius:5px;"></div><p style="margin-bottom:0px;"> <div id="augmentEquipSelectedItemDiv" class="mb-3" style="background:${backgroundColor};display:inline-block;text-align:left;margin-right:10px;border:2px #475254 solid;padding:10px;border-radius:5px;"></div> <div style="display:inline-block;vertical-align:top;"> </div> </div> <div style="display:inline-block;vertical-align:top;"> <div id="selectedAugDiv"> </div> <div style="display:inline-block;"> <button class="btn-mm btn-aug" id="btnAddAugment">Add Augment</button><br> <button class="btn-mm btn-aug" id="btnAutoSelectThisType">Auto Select This Type</button><br> <button class="btn-mm btn-aug" id="btnClearAutoSelect">Clear Auto Select Type</button> <center> <div id="btnAddNewSlot" class="item-action"> <img src="https://torax.outwar.com/images/addaugs.jpg" class="btn-item" onmouseover="statspopup(event,'<b>MoxxiMod+</b><br>Add Augment Slot')" onmouseout="kill()"> <div id="cntAddNewSlot" class="item-count"></div> </div> <div id="btnRemoveAug" class="item-action"> <img src="https://torax.outwar.com/images/items/AugmentRemover.gif" class="btn-item" onmouseover="statspopup(event,'<b>MoxxiMod+</b><br>Remove Augment')" onmouseout="kill()"> <div id="cntRemoveAug" class="item-count"></div> </div> <div id="btnRemoveAllAugs" class="item-action"> <img src="https://torax.outwar.com/images/items/AllAugmentRemover.png" class="btn-item" onmouseover="statspopup(event,'<b>MoxxiMod+</b><br>Remove All Augments')" onmouseout="kill()"> <div id="cntRemoveAllAugs" class="item-count"></div> </div> </div> </div> </div> <div class="widget profile-widget mb-3"> <h4>EQUIPPED AUG DATA</h4> <div id="equippedAugDataDiv"><img src="https://studiomoxxi.com/moxximod/loading-gif.gif" style="width:35px;height:35px;"></div> </div> </div> </div> </div> ` // Add event listener to items. Built as function to allow for refresh once data table loads const selectableItems = document.querySelectorAll('img.selectable-item'); for (let item of selectableItems){ item.addEventListener('click', async function itemClick(event) { // Remove the event listener item.removeEventListener('click', itemClick); // Hide the selected slotted aug div document.querySelector("#augmentEquipSlottedAug").style.display = "none"; // Load item in widget div const itemId = item.outerHTML.match(/event,'([0-9]+)'/i)[1]; await augmentEquipItemHandler(itemId); await getItemCounts(); // Re-add the event listener item.addEventListener('click', itemClick); }); }; // Button: Add Augment document.querySelector("#btnAddAugment").addEventListener('click', async function(){ addAugment(); }); document.addEventListener("keydown", function(event) { if (event.ctrlKey && event.code === "Space") { addAugment(); }; }); async function addAugment(){ const itemDiv = document.querySelector("#selectedItemId"); const augDiv = document.querySelector("#selectedAugId"); const dropDown = document.querySelector("#selectAugDropdown").value; if (!itemDiv){ alert('Please select an item before trying to add an augment'); return; }; if (!dropDown.match(/slot"([^"]*)"/i)){ alert('Please select an augment slot from the dropdown menu'); return; }; if (!augDiv){ alert('Please select an augment'); return; }; if (dropDown.match(/id"([^"]*)"/i)){ const confirm = window.confirm('Are you sure you want to overwrite the existing augment?'); if (!confirm){ return; }; }; const itemId = itemDiv.innerHTML const augId = augDiv.innerHTML const slotNum = dropDown.match(/slot"([^"]*)"/i)[1]; // Slot the augment const fetchNonce = await superfetch(`getiteminfo.php?id=${itemId}&augmentid=${augId}`); const formNonce = fetchNonce.match(/name="form-nonce" value="([^"]*)"/i)[1]; const postBody = `form-itemid=${itemId}&form-augmentid=${augId}&form-slotid=${slotNum}&form-nonce=${formNonce}&form-submitted=Augment+Item%21` await superpost('augmentequip', postBody); document.querySelector("#augmentEquipSelectedItemDiv").innerHTML = '<img src="https://studiomoxxi.com/moxximod/loading-gif.gif">' // Remove aug from list of available augs document.querySelector(`#augment-${augId}`).remove(); // Clear selected elements document.querySelector("#selectedAugDiv").innerHTML = ''; document.querySelector("#augmentEquipSlottedAug").style.display = "none"; // Refresh the item div await augmentEquipItemHandler(itemId); } // Button: Add New Slot if (GM_getValue("auth").match("Full")){ document.querySelector("#btnAddNewSlot").addEventListener('click', async function(){ const itemDiv = document.querySelector("#selectedItemId"); if (!itemDiv){ alert('Please select an item before trying to add an augment'); return; }; const confirm = window.confirm('Are you sure you want to add an augment slot to this item?'); if (!confirm){ return; }; const itemId = itemDiv.innerHTML const postBody = `item=${itemId}&submit=Submit` const post = await superpost('addaug', postBody); if (post.match('This item already has 5 augment slots')){ alert('Error: This item already had 5 augment slots'); return; }; if (post.match('You do not have an add aug item')){ alert('Error: You do not have an add aug item'); return; }; await augmentEquipItemHandler(itemId); await getItemCounts(); }); } else { document.querySelector("#btnAddNewSlot > img").setAttribute('style','opacity:0.33'); }; // Button: Remove Aug if (GM_getValue("auth").match("Full")){ document.querySelector("#btnRemoveAug").addEventListener('click', async function(){ const itemDiv = document.querySelector("#selectedItemId"); const dropDown = document.querySelector("#selectAugDropdown").value; if (!itemDiv){ alert('Please select an item before trying to remove augments'); return; }; if (!dropDown.match(/id"([^"]*)"/i)){ alert('Please select an augment slot from the dropdown menu'); return; }; const confirm = window.confirm('Are you sure you want to remove this augment?'); if (!confirm){ return; }; const itemId = itemDiv.innerHTML const slotNum = dropDown.match(/slot"([^"]*)"/i)[1]; const postBody = `form-itemid=${itemId}&form-slotid=${slotNum}&form-removesubmitted=Remove+Augment%21` const post = await superpost('augmentremove', postBody); if (post.match('You do not own the item required to use this page')){ alert('Error: You do not have a remove augment item'); return; }; document.querySelector("#augmentEquipSlottedAug").style.display = "none"; await augmentEquipItemHandler(itemId); await getItemCounts(); }); } else { document.querySelector("#btnRemoveAug > img").setAttribute('style','opacity:0.33'); }; // Button: Remove All Augs if (GM_getValue("auth").match("Full")){ document.querySelector("#btnRemoveAllAugs").addEventListener('click', async function(){ const itemDiv = document.querySelector("#selectedItemId"); if (!itemDiv){ alert('Please select an item before trying to remove augments'); return; }; const confirm = window.confirm('Are you sure you want to remove all the augments from this item?'); if (!confirm){ return; }; const itemId = itemDiv.innerHTML const postBody = `form-itemid=${itemId}&form-slotid=1&form-removeall=Remove+ALL+Augments%21` const post = await superpost('augmentremove', postBody); if (post.match('You do not own the item required to use this page')){ alert('Error: You do not have a remove all augments item'); return; }; await augmentEquipItemHandler(itemId); await getItemCounts(); }); } else { document.querySelector("#btnRemoveAllAugs > img").setAttribute('style','opacity:0.33'); }; // Button: Auto Select This Type document.querySelector("#btnAutoSelectThisType").addEventListener('click', async function(){ const selectedAug = document.querySelector("#selectedAugDiv").innerHTML.match(/img src="([^"]*)"/i); if (!selectedAug){ alert('Please select an augment'); return; }; const selectedAugImg = selectedAug[1]; GM_setValue('autoSelectAug', selectedAugImg); alert('Autoselect saved'); }); // Button: Clear Auto Select document.querySelector("#btnClearAutoSelect").addEventListener('click', async function(){ GM_deleteValue('autoSelectAug'); alert('Cleared auto select'); }); // Equipped aug data const equippedAugDataRows = []; async function getAugs(item){ const itemId = item.match(/event,'([0-9]+)'/i)[1]; const itemData = await superfetchItem(itemId); const itemImg = itemData.img; const augIdArray = itemData.augids; async function getAugData(augId){ const augData = await superfetchItem(augId); const augImg = augData.img; const augEle = augData.ele; const augPower = augData.atk + augData.hp; const augChaos = augData.chaosdmg; const augMr = augData.maxrage; const augVile = augData.vile; const augRatio = (augData.maxrage / augData.rpt).toFixed(1); equippedAugDataRows.push(` <tr> <td><img src="${augImg}" onmouseover="itempopup(event,'${augId}')" onmouseout="kill()"></td> <td><img src="${itemImg}" class="selectable-item-table" alt="event,'${itemId}'"></td> <td>${augEle.toLocaleString()}</td> <td>${augPower.toLocaleString()}</td> <td>${augChaos}</td> <td>${augMr.toLocaleString()}</td> <td>${augVile.toLocaleString()}</td> <td>${augRatio}</td> </tr> `) }; await Promise.all(augIdArray.map(getAugData)); }; await Promise.all(equippedArray.map(getAugs)); document.querySelector("#equippedAugDataDiv").innerHTML = ` <table class="table table-striped sortable" id="equippedAugDataTable"> <thead><tr><th>Aug</th><th>Item</th><th>Ele</th><th>Power</th><th>Chaos</th><th>Max rage</th><th>vile</th><th>rpt:mr</th></tr></thead> <tbody>${equippedAugDataRows.join('')}</tbody> </table> ` const selectableItemsTable = document.querySelectorAll('img.selectable-item-table'); for (let item of selectableItemsTable){ item.addEventListener('click', async function itemClick(event) { // Remove the event listener item.removeEventListener('click', itemClick); // Hide the selected slotted aug div document.querySelector("#augmentEquipSlottedAug").style.display = "none"; // Load item in widget div const itemId = item.outerHTML.match(/event,'([0-9]+)'/i)[1]; await augmentEquipItemHandler(itemId); await getItemCounts(); // Re-add the event listener item.addEventListener('click', itemClick); }); }; await sortableTables(); // Function to handle adding item information and refreshing after changes async function augmentEquipItemHandler(itemId){ const rollover = await superfetch(`item_rollover.php?id=${itemId}`, true); // Set to true for allow for data refresh document.querySelector("#augmentEquipSelectedItemDiv").innerHTML = ` Selected Item: <span id="selectedItemId">${itemId}</span> <br> ${rollover} ` // Add empty dropdown menu document.querySelector("#slottedAugsDropdownMenu").innerHTML = `<select class="form-control" id="selectAugDropdown"><option value="" disabled selected hidden>Select slot...</option></select>` // Check for open slot to auto-select from the dropdown const firstOpenSlot = []; // Grab slotted augs and aug slots const itemAugs = rollover.match(/(event,'[0-9]+_[0-9]+'|augslot\.jpg)/g); if (itemAugs){ for (var i = 0; i < itemAugs.length; i++){ const string = itemAugs[i]; if (string == "augslot.jpg"){ const newOption = document.createElement('option'); newOption.value = `slot"${i + 1}"`; newOption.text = `${i + 1}. Open augment slot`; document.querySelector("#selectAugDropdown").add(newOption) if (firstOpenSlot.length == 0){ firstOpenSlot.push(`slot"${i + 1}"`); }; } else { const augId = string.match(/[0-9]+_[0-9]+/i); const fetchAug = await superfetch(`item_rollover.php?id=${augId}`, true); const augName = (fetchAug.match(/align="left">(.*?)<\/td>/i) || ['',''])[1].replace('td colspan="2"','a'); const newOption = document.createElement('option'); newOption.value = `slot"${i + 1}" id"${augId}"`; newOption.text = `${i + 1}. ${augName}`; document.querySelector("#selectAugDropdown").add(newOption) }; }; }; // Auto change the dropdown menu to the first available open aug slot if one was found if (firstOpenSlot[0]){ document.querySelector("#selectAugDropdown").value = firstOpenSlot[0]; }; // Dropdown menu function document.querySelector("#selectAugDropdown").addEventListener('change', async function(){ const selectedValue = this.value; const selectedSlot = selectedValue.match(/slot"([^"]*)"/i)[1]; const selectedAugId = selectedValue.match(/id"([^"]*)"/i) if (selectedAugId){ const augId = selectedAugId[1]; const fetchAug = await superfetch(`item_rollover.php?id=${augId}`); document.querySelector("#augmentEquipSlottedAug").innerHTML = `Slotted Augment (slot ${selectedSlot})<br>` + fetchAug; document.querySelector("#augmentEquipSlottedAug").style.display = "inline-block" } else { document.querySelector("#augmentEquipSlottedAug").innerHTML = `Unused augment slot ${selectedSlot}`; } }); // Revert display document.querySelector("#showHideDiv").style.display = "block"; // Add event listeners to each augment const selectableAugs = document.querySelectorAll('img.selectable-aug'); for (let aug of selectableAugs){ aug.addEventListener('click', async function(){ const augId = aug.outerHTML.match(/event,'([0-9]+)'/i)[1]; const rollover = await superfetch(`item_rollover.php?id=${augId}`); document.querySelector("#selectedAugDiv").innerHTML = `<div class="mb-3" style="background:${backgroundColor};display:inline-block;text-align:left;margin-right:10px;border:2px #475254 solid;padding:10px;border-radius:5px;"> Selected augment: <span id="selectedAugId">${augId}</span><br> ${rollover} ` }); }; // Select auto select aug if saved if (GM_getValue('autoSelectAug')){ const augToAutoSelect = GM_getValue('autoSelectAug'); const lookForAug = Array.from(document.querySelectorAll("#availableAugsDiv img")).find(img => img.src.includes(augToAutoSelect)); if (lookForAug){ lookForAug.click(); }; }; }; async function getItemCounts(){ let addAug = 0; let removeAug = 0; let removeAllAug = 0; const vault = await superfetch('vault',true); if (vault.match(/addaugs\.jpg/g)){ addAug += vault.match(/addaugs\.jpg/g).length }; if (vault.match(/AugmentRemover\.gif/g)){ removeAug += vault.match(/AugmentRemover\.gif/g).length }; if (vault.match(/AllAugmentRemover\.png/g)){ removeAllAug += vault.match(/AllAugmentRemover\.png/g).length }; document.querySelector("#cntAddNewSlot").innerHTML = addAug + "x"; document.querySelector("#cntRemoveAug").innerHTML = removeAug + "x"; document.querySelector("#cntRemoveAllAugs").innerHTML = removeAllAug + "x"; }; await toolTip('Ctrl + Space to add an augment to an item once selected'); }; // Add augment rebuild async function augmentEquipOLD(server){ // Remove header document.querySelector("#content-header-row > div > div:nth-child(1)").setAttribute('style','display:none;'); // Set widget class document.querySelector("#content-header-row > div > div:nth-child(2)").classList.add("widget"); // Force scroll document.body.setAttribute('style','overflow-y:scroll;') // Use wasAnAugmentClicked variable to prevent infinite click loop let wasAnAugmentClicked = false; const addAugText = document.querySelector("#content-header-row > div > div:nth-child(2) > div:nth-child(2) > div:nth-child(2)") addAugText.innerHTML = addAugText.innerHTML.replace(/You have no augments in this category/g,'') var augChange = document.querySelector("#item-container") const addAugWidget = document.querySelector("#content-header-row > div > div.row.widget > div:nth-child(2) > div:nth-child(1)") let observer = new MutationObserver(mutationRecords => { addAug(); }); observer.observe(augChange, { childList: true, subtree: true, characterDataOldValue: true }); addAug(); function addAug(){ // Auto select the first available open augment const selectedAug = document.querySelector('h3[style="text-align:center;"]') if (document.querySelector("#item-container").innerHTML.match(/src="\/images\/augslot\.jpg"><h5>Slot ([0-9]+)<\/h5>/i) && selectedAug){ var openAug = document.querySelector("#item-container").innerHTML.match(/src="\/images\/augslot\.jpg"><h5>Slot ([0-9]+)<\/h5>/i)[1] document.querySelector("#form-slotid").value = openAug; }; // Get saved aug information and auto select the same aug image if stored if (addAugWidget && !wasAnAugmentClicked){ if (GM_getValue("savedAug")){ const savedAugImg = GM_getValue("savedAug") const savedAugLookup = document.querySelectorAll(`img[src="${savedAugImg}"]`) for (let aug of savedAugLookup){ if (aug.hasAttribute('onclick') && !wasAnAugmentClicked){ aug.click(); wasAnAugmentClicked = true; }; }; }; }; // If an item and aug has been selected, create the save aug and clear buttons if (addAugWidget && !document.querySelector("#saveAugType") && selectedAug){ const newDivElement = document.createElement("div"); newDivElement.setAttribute('id','saveAugType'); newDivElement.setAttribute('class','col-inner'); newDivElement.setAttribute('style','padding:10px;'); newDivElement.innerHTML = `<button id="saveAugName" class="btn btn-primary" style="margin-rght:15px;">Auto Select This Aug</button><button id="clearAugName" class="btn btn-primary" style="margin-left:15px;">Clear Save</button><p>` addAugWidget.appendChild(newDivElement); document.querySelector("#saveAugName").addEventListener("click", function(){ const augImgName = document.querySelector("#item-container").innerHTML.match(/Selected Augment.*[\n\r].*/).toString().match(/src="([^"]*)"/i)[1]; GM_setValue("savedAug", augImgName); alert('Will auto-select this augment when adding augs') }); document.querySelector("#clearAugName").addEventListener("click", function(){ GM.deleteValue("savedAug") alert('Saved augment has been cleared') }); } } }; // Trade page rebuild async function trade(){ // Don't run function if page is trade completion message or if on sales log page or trade shoutbox page if (document.body.innerHTML.match('This trade has been completed!') || url.match("usertradelog") || url.match("tradepost")){ return; }; // Get the name of the character trading with const otherName = document.body.innerHTML.replace(/[\n\r]/g,'').replace(/\s+/g,'').match(/<h1>Tradingwith(.*?)<\/h1>/i)[1]; // Styling GM_addStyle(` #divTrade > div > div > img {min-width:50px;min-height:50px;margin-bottom:7px;} #divTrade > div > div > p {position:relative;top:0px;height:15px;width:50px;left:0px;font-size:10px !important;border: SOLID 1px #2B2B2B;} button.btn-mm{font-size:11px;padding:0.5rem;} #otherGuy > a{color:#00CC00;} #otherGuy > a:hover{color:#FFFFFF;} `); // Fetch myaccount const myaccount = await superfetch('myaccount'); // Remove empty slot categories const slotDiv = document.querySelectorAll("#divTrade > div") const removeNoItems = async (div) => { if (div.innerHTML.toString().match('You have no items in this category')){ div.remove(); }; }; await Promise.all([...slotDiv].map(removeNoItems)); // Remove all headers const slotHeader = document.querySelectorAll("#divTrade > div > h1") const removeHeaders = async (header) => { header.remove(); }; await Promise.all([...slotHeader].map(removeHeaders)); // Format quantity values const quantityP = document.querySelectorAll("#divTrade > div > div > p") const formatQuantity = async (qnt) => { qnt.setAttribute('class','form-control-new') }; await Promise.all([...quantityP].map(formatQuantity)); // Modify class list of trade windows to make them smaller const myOffer = document.querySelector("#divTradeMyOffer"); const theirOffer = document.querySelector("#divTradeTheirOffer"); if (myOffer && theirOffer){ myOffer.classList.remove('col-lg-6') theirOffer.classList.remove('col-lg-6') myOffer.classList.add('col-lg-5') theirOffer.classList.add('col-lg-5') }; // Create settings div var parentDiv = document.querySelector("#divTradeData") var newDiv = document.createElement("div"); newDiv.className = "col-12 col-lg-2"; newDiv.setAttribute('style','padding:19px;display:inline-block;overflow:hidden;') newDiv.innerHTML = ` <strong>Options</strong> <div class="divBlackBox statbox widget box box-shadow" style="border: 4px solid #2B2B2B;"> Default Qnty<input style="width:120px;" id="defQnty" type="text" class="form-control-new" autocomplete="off"> <div class="btn-group show" role="group" style="margin-top:1rem;"> <button type="button" class="btn-mm dropdown-toggle" data-toggle="dropdown">SELECT <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-down"><polyline points="6 9 12 15 18 9"></polyline></svg></button> <div class="dropdown-menu" style="will-change: transform; position: absolute; transform: translate3d(0px, 38px, 0px); top: 0px; left: 0px;"> <a class="dropdown-item select-all" id="all" href="javascript:void(0);">ALL ITEMS</a> <a class="dropdown-item select-all" id="blackhand" href="javascript:void(0);">BLACKHAND ITEMS</a> <a class="dropdown-item select-all" id="dragon" href="javascript:void(0);">DRAGON ITEMS</a> <a class="dropdown-item select-all" id="basicaugs" href="javascript:void(0);">BASIC AUGS</a> </div> </div> <p> <img src="https://studiomoxxi.com/moxximod/toolbareq.png" style="margin-right:0.5rem;cursor:pointer;" onmouseover="statspopup(event,'<b>${otherName}s Equipment<b>')" onmouseout="kill()" id="theirEq"> </div>` // Look for trade partner on RGA and build link if (document.body.innerHTML.match('You have accepted this trade')){ const parseData = new DOMParser(); const myAccount = parseData.parseFromString(myaccount, 'text/html'); const charsTable = myAccount.querySelector("#zero-config").innerHTML; const otherId = document.body.innerHTML.match(/name="tradeWith" value="([0-9]+)"/i)[1] const idMatch = new RegExp(`outwar\\.com\/world\\?suid=${otherId}`); if (charsTable.match(idMatch)){ document.querySelector("#divTradeMyOffer > div:nth-child(4)").innerHTML = ` <div id="otherGuy"> <a href="trade?suid=${otherId}">Go to ${otherName}</a> </div> ` }; }; // Class target lists const lists = { blackhand: ['Soul of Blackhand Reborn','Myrmidon Helm Reborn','Trinket of Aridity Reborn','Blackhand Reborn','Prophecy Mail Reborn','Incredible Tower Shield Reborn','Cord of Freezing Winds Reborn','Interstellar Leggings Reborn','Ring of the Sea Reborn','Boots of the Eagle Reborn'], basicaugs: ['Augment'], dragon: ['Eye of Dalinda','Sinister Dragon Helm','Blade of Xiuhcoatl','Winged Serpents Armor','Gem Scale Shield','Encased Serpents Soul','Strap of Ananta Boga','Studded Legs of Draco','Horned Dragon Boots'], }; // Select all individual item divs const divItem = document.querySelectorAll(".divItem"); // Loop through item divs for (let i = 0; i < divItem.length; i++) { // Get item name const itemName = divItem[i].outerHTML.match(/alt="([^"]*)"/i)[1]; // Add "all" class for trading all items if (!itemName.match('Amulet Chest')){ divItem[i].classList.add("all"); }; // Check if item name is contained in any auto-select buttons for (const key in lists){ if (lists[key].includes(itemName)){ // If found in auto-select button list, add class name divItem[i].classList.add(key); }; }; // Add event listener to update trade quantity to saved value divItem[i].addEventListener("click", async function() { // Get the total number of the item available to trade const maxItems = parseInt(divItem[i].outerHTML.match(/event, '[^']*', '[^']*', '[^']*', '([0-9]+)'/i)[1]); // If more than 1 item if (maxItems > 1){ // Set value of quantity box based on saved default value but set to maxItems if default value is too large let setValue = GM_getValue("tradeQuantity") if (setValue > maxItems){ setValue = maxItems; }; // Inject the setValue if (GM_getValue("tradeQuantity")){ document.querySelector("#cauldronqtyspinner").value = setValue; }; }; }); }; // Add options div var child = parentDiv.firstElementChild.nextElementSibling; parentDiv.insertBefore(newDiv, child); // Select all buttons const selectAllButtons = document.querySelectorAll(".select-all"); for (let i = 0; i < selectAllButtons.length; i++) { var button = selectAllButtons[i]; const buttonId = button.id // Add event listener to the button button.addEventListener("click", async function() { // Get all individual item divs based on ID const items = document.querySelectorAll(`.${buttonId}`) // Loop through item divs const selectAll = async (item) => { // Build trade perameters const maxItems = parseInt(item.outerHTML.match(/event, '[^']*', '[^']*', '[^']*', '([0-9]+)'/i)[1]); const tradeWith = document.body.innerHTML.match(/name="tradeWith" value="([0-9]+)"/i)[1] const isCrewTrade = document.body.innerHTML.match(/id="isCrewTrade" value="([0-9]+)"/i)[1] const itemId = item.outerHTML.match(/event, '([^']*)', '[^']*', '[^']*'/i)[1]; const itemHash = item.outerHTML.match(/event, '[^']*', '[^']*', '([^']*)'/i)[1]; // Send get request const response = await fetch(`trade.php?tradeWith=${tradeWith}&isCrewTrade=${isCrewTrade}&qty=${maxItems}&addItem=${itemId}&addHash=${itemHash}`, { method: 'GET', headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }); if (response){ window.location.href = `trade?isCrewTrade=${isCrewTrade}` }; }; await Promise.all([...items].map(selectAll)); }); }; // Set default quantity logic const quantityText = document.querySelector("#defQnty") if (GM_getValue('tradeQuantity')){ quantityText.value = GM_getValue('tradeQuantity'); }; document.querySelector("#defQnty").addEventListener('input', async function(){ const defaultQuantityValue = quantityText.value GM_setValue('tradeQuantity',defaultQuantityValue); }); // Their equipment document.querySelector("#theirEq").addEventListener('click', async function(){ const tradeWith = document.body.innerHTML.match(/name="tradeWith" value="([0-9]+)"/i)[1] const profileData = await superfetchProfile(`profile?id=${tradeWith}`); createWindow(`${otherName}'s Equipment`, "their_eq", 300, 100, 0); document.querySelector("#their_eq_content").innerHTML = profileData.thedude.replace(/<div class="[^"]*">/g,''); }) }; // Brawl page rebuild async function closedpvp(profileData,server,charid){ GM_addStyle(` #content-header-row > div:nth-child(3) > div{height:100%} #content-header-row > div:nth-child(2) > div:nth-child(1) > div{left:25px;} .brawl-skills > img {width: 30px;height: 30px;margin: 2px;border-radius: 5px;border: 2px #475254 SOLID;} img.nwo-brawl-icon{height:15px;width:15px;margin-right:0.5rem;} `) // Active brawl formatting if (document.body.innerHTML.match('BRAWL IS ACTIVE') && document.body.innerHTML.match('You are entered into the brawl')){ // Player rows const playerRows = document.querySelectorAll("#content-header-row > div:nth-child(4) > div > table > tbody > tr"); // Remove unwanted elements document.querySelector("#content-header-row > div:nth-child(3) > div:nth-child(2)").remove(); document.querySelector("#content-header-row > div:nth-child(3) > div.widget-content.widget-content-area.mt-3").remove(); document.querySelector("#content-header-row > h2").remove(); document.querySelector("#content-header-row > div:nth-child(2) > div > ul").remove(); document.querySelector("#content-header-row > div:nth-child(2) > div > p").remove(); document.querySelector("#content-header-row > div:nth-child(2) > div > h4").remove(); if (document.querySelector("#content-header-row > div:nth-child(2) > div > h3")){ document.querySelector("#content-header-row > div:nth-child(2) > div > h3").remove(); }; // Add your skills div and content const allCast = profileData.skills.images; const yourName = profileData.name; const yourPower = profileData.power; const yourCrew = profileData.crewid; document.querySelector("#content-header-row > div:nth-child(2) > div.widget-content.widget-content-area.mt-3").innerHTML = ` <h5>Your Power</h5> ${yourPower.toLocaleString()}<hr> <h5>Your skills</h5> <div class="brawl-skills">${allCast.join(' ')}</div><hr> <button class="btn-mm" id="hideAttacked" style="margin-bottom:10px;">HIDE ATTACKED PLAYERS</button> <button class="btn-mm" id="showAll" style="margin-bottom:10px;">SHOW ALL PLAYERS</button> ` document.querySelector("#content-header-row > div:nth-child(3)").innerHTML = ` <div class="widget-content widget-content-area"> <img src="https://studiomoxxi.com/moxximod/bot.png"> <hr> <h5> Sunder armor</h5> <input style="width:135px;" id="saTarget1" type="text" class="form-control-new" autocomplete="off"> <input style="width:135px;" id="saTarget2" type="text" class="form-control-new" autocomplete="off"> <input style="width:135px;" id="saTarget3" type="text" class="form-control-new" autocomplete="off"><br> <button class="btn-mm" id="castSunderArmor" style="margin-top:10px;">CAST SUNDER ARMOR</button><hr> <h5 style="margin-top:1rem;">Poison dart</h5> <input style="width:135px;" id="pdTarget" type="text" class="form-control-new" autocomplete="off"><br> <button class="btn-mm" id="castPoisonDart" style="margin-top:10px;">CAST POISON DART</button> </div> ` let saCharging; let pdCharging; const sunderArmor = await superfetch('skills_info.php?id=21'); const saCooldown = sunderArmor.match(/This skill is recharging\. [0-9]+ minutes remaining\./i); const saUnknown = sunderArmor.match(/You have not learned this skill yet/i); if (saCooldown || saUnknown){ document.querySelector("#castSunderArmor").outerHTML = `<br>Sunder armor isn't available to cast` saCharging = true; } const poisonDart = await superfetch('skills_info.php?id=16'); const pdCooldown = poisonDart.match(/This skill is recharging\. [0-9]+ minutes remaining\./i); const pdUnknown = poisonDart.match(/You have not learned this skill yet/i); if (pdCooldown || pdUnknown){ document.querySelector("#castPoisonDart").outerHTML = `<br>Poison dart isn't available to cast` pdCharging = true; } // Select the parent element var parentElement = document.querySelector("#content > div.layout-px-spacing"); // Create a new div element for player table var newDiv = document.createElement("div"); newDiv.innerHTML = ` <table id="brawlTable" class="table table-striped"> <thead><tr><th>Rank</th><th>Player</th><th>Prize</th><th>Power</th><th>Wins</th><th>Damage</th><th>Skills</th><th>Atks</th><th>Attack</th><th>Wait</th></thead> <tbody></tbody> </table>`; newDiv.setAttribute('class','row justify-content-center widget-content widget-content-area'); newDiv.setAttribute('style','margin-top:1rem;margin-left:2px;margin-right:2px;'); parentElement.appendChild(newDiv); // Build new player table let data = []; const buildTable = async (item,index) => { const row = item.innerHTML.replace(/,/g,'').replace(/\s+/g,'').replace(/[\n\r]/g,''); const brawlPlayerid = row.match(/id=([0-9]+)/i)[1]; const targetProfileData = await superfetchProfile(`profile?id=${brawlPlayerid}`); const brawlRank = parseInt(row.match(/<td>([0-9]+)\.<\/td>/i)[1]); let brawlPrize = ''; if (brawlRank == 1){ brawlPrize = '20 coins'} else if (brawlRank == 2){ brawlPrize = '17 coins'} else if (brawlRank == 3){ brawlPrize = '15 coins'} else if (brawlRank == 4){ brawlPrize = '13 coins'} else if (brawlRank == 5){ brawlPrize = '11 coins'} else if (brawlRank <= 8){ brawlPrize = '10 coins'} else if (brawlRank <= 11){ brawlPrize = '9 coins'} else if (brawlRank <= 14){ brawlPrize = '8 coins'} else if (brawlRank <= 17){ brawlPrize = '7 coins'} else if (brawlRank <= 20){ brawlPrize = '6 coins'} else if (brawlRank <= 25){ brawlPrize = '5 coins'} else if (brawlRank <= 30){ brawlPrize = '4 coins'} const brawlPlayer = row.match(/<ahref="profile\?id=[0-9]+">(.*?)<\/a>/i)[1]; const parsePower = targetProfileData.power; const parseCrew = targetProfileData.crewid; const nwoCheck = parseCrew == 6637 && yourCrew == 6637 ? `<img src="https://studiomoxxi.com/moxximod/nwo-server-icon.png" class="nwo-brawl-icon">` : '' let brawlPlayerpower; if (parsePower == 0){brawlPlayerpower = "suspended" } else if (Math.abs(yourPower-parsePower) <= (yourPower*0.0375)){brawlPlayerpower = `<font color="#ffea00">${parsePower.toLocaleString()}</font>`} else if ((parsePower-yourPower) >= (yourPower*0.0375)){brawlPlayerpower = `<font color="#d40000">${parsePower.toLocaleString()}</font>`} else if ((yourPower-parsePower) >= (yourPower*0.0375)){brawlPlayerpower = `<font color="#00dc24">${parsePower.toLocaleString()}</font>`}; const brawlString = row.replace(/<ahref.*?<\/a>/g,'').match(/<td>([0-9]+)<\/td><td>([0-9]+)<\/td><td>(.*?)<\/td>/i); const brawlWins = brawlString[1]; const brawlDamage = parseInt(brawlString[2]).toLocaleString(); const brawlYourattacks = brawlString[3]; const brawlCop = targetProfileData.skills.list.includes("Circle of Protection") ? `<font color="#ffea00">Circle of Protection: ${targetProfileData.skills.images.toString().match(/Reduce base damage taken in PvP combat by [0-9]+%\.<br \/>(.*?)<br>/i)[1]}</font>` : ''; const brawlCopstring = brawlCop.match(/: .*?</i) ? `${brawlCop.match(/: .*?</i).toString().replace(/:\s*|\s+|min|</gi, '').replace(/s/g,'')}` : '' const displaySkills = []; const castByMeRegex = new RegExp(`Cast By ${yourName}`); const allCastSkills = targetProfileData.skills.images; if (brawlPlayer == yourName){ displaySkills.push('-'); } else { allCastSkills.forEach(skill => { if (skill.match(castByMeRegex) || skill.match("Circle of Protection")){ displaySkills.push(skill); }; }); }; const brawlAttackbtn = brawlYourattacks.match('10') || brawlYourattacks.match('-') ? '' : `<button class="btn-mm attack-10x" id="" alt="${brawlPlayerid}">ATTACK 10X</button>`; const brawlWaitforcop = brawlYourattacks.match('10') || brawlCop == '' ? '' : `<button class="btn-mm wait-for-cop" alt="${brawlPlayerid}|${brawlCopstring}">WAIT FOR COP</button>`; data.push(rowData = { rank: brawlRank, player: `${nwoCheck}<a href="profile?id=${brawlPlayerid}">${brawlPlayer}</a>`, prize: brawlPrize, power: brawlPlayerpower, wins: brawlWins, damage: brawlDamage, skills: `<div class="brawl-skills">` + displaySkills.join('') + `</div>`, attacks: `<span class="attacked-${brawlYourattacks}">${brawlYourattacks}</span>`, attack10x: brawlAttackbtn, wait: brawlWaitforcop }); }; await Promise.all(Array.from(playerRows).map((raid, index) => buildTable(raid, index))); // Add rows to table data.sort((a, b) => a.rank - b.rank); data.forEach(rowData => { const rowElement = document.querySelector("#brawlTable > tbody").insertRow(); rowElement.innerHTML = Object.values(rowData).map(value => `<td>${value}</td>`).join(''); }); // Hide attacked players on load unless all attacks are completed if (data.length-1 != document.querySelectorAll('.attacked-10').length){ document.querySelectorAll('.attacked-10').forEach(function(element) { element.closest('tr').style.display = 'none'; }); }; // Button: Hide attacked players document.querySelector("#hideAttacked").addEventListener('click', async function(){ document.querySelectorAll('.attacked-10').forEach(function(element) { element.closest('tr').style.display = 'none'; }); }); // Button: Show all players document.querySelector("#showAll").addEventListener('click', async function(){ document.querySelectorAll('.attacked-10').forEach(function(element) { element.closest('tr').style.display = 'revert'; }); }); // Button: Cast sunder armor if (!saCharging){ document.querySelector("#castSunderArmor").addEventListener('click', async function(){ const target1 = document.querySelector("#saTarget1").value.replace(/\t/g,'') const target2 = document.querySelector("#saTarget2").value.replace(/\t/g,'') const target3 = document.querySelector("#saTarget3").value.replace(/\t/g,'') const body = `target%5B%5D=${target1}&target%5B%5D=${target2}&target%5B%5D=${target3}&castskillid=21&cast=Cast+Skill` if (GM_getValue('auth').match("Full")){ await superpost(`cast_skills.php?C=6`,body); window.location = window.location; } else { displayAuthStatus(GM_getValue('auth')); }; }); }; // Button: Cast poison dart if (!pdCharging){ document.querySelector("#castPoisonDart").addEventListener('click', async function(){ const target = document.querySelector("#pdTarget").value const body = new URLSearchParams({ 'target[]': target, 'castskillid': '16', 'cast': 'Cast Skill' }); if (GM_getValue('auth').match("Full")){ await superpost(`cast_skills.php?C=6`,body); window.location = window.location; } else { displayAuthStatus(GM_getValue('auth')); }; }); }; // Button: Attack 10 times const attack10x = document.querySelectorAll(".attack-10x"); for (let i = 0; i < attack10x.length; i++) { var button = attack10x[i]; const atkCharId = button.outerHTML.match(/alt="([0-9]+)"/i)[1]; button.addEventListener("click", async function() { await mmplus(`Atk10x|rganame|${server}|${charid}|${atkCharId}`); }); }; // Button: Wait for cop const waitForCop = document.querySelectorAll(".wait-for-cop"); for (let i = 0; i < waitForCop.length; i++) { var button = waitForCop[i]; const atkCharId = button.outerHTML.match(/alt="([0-9]+)\|.*?"/i)[1]; const atkCopTime = button.outerHTML.match(/alt=".*?\|(.*?)"/i)[1]; button.addEventListener("click", async function() { await mmplus(`AtkCOP|rganame|${server}|${charid}|${atkCharId}|${atkCopTime}`); }); }; } else if (window.location.search == "?type=1"){ // Get a count for number of participants per each faction const tables = document.querySelectorAll("table"); const factionObj = { Alvar: 0, Vordyn: 0, Delruk: 0 }; Array.from(tables).filter(table => { return Array.from(table.querySelectorAll("tr")).some(tr => { const tds = tr.querySelectorAll("td"); if (tds[2]){ const faction = tds[2].innerHTML.match(/title="([^"]*)"/i); if (faction){ factionObj[faction[1]] += 1; }; }; }); }); const totAlvar = factionObj.Alvar const totDelruk = factionObj.Delruk const totVordyn = factionObj.Vordyn // Add participant faction counts const ul = document.querySelector("ul.striped-list"); ul.querySelectorAll("li")[2].remove(); const liAlvar = document.createElement("li"); liAlvar.innerHTML = `<font color="#f6b861"><b>Delruk Participants:</b> ${totDelruk}`; liAlvar.classList.add('list-group-item') liAlvar.classList.add('pl-3'); ul.appendChild(liAlvar); const liDelruk = document.createElement("li"); liDelruk.innerHTML = `<font color="#b7d9fb"><b>Alvar Participants:</b> ${totAlvar}`; liDelruk.classList.add('list-group-item') liDelruk.classList.add('pl-3'); ul.appendChild(liDelruk); const liVordyn = document.createElement("li"); liVordyn.innerHTML = `<font color="#ffb7f1"><b>Vordyn Participants:</b> ${totVordyn}`; liVordyn.classList.add('list-group-item') liVordyn.classList.add('pl-3'); ul.appendChild(liVordyn); const liTotal = document.createElement("li"); liTotal.innerHTML = `<b>Total Participants:</b> ${totDelruk + totAlvar + totVordyn}`; liTotal.classList.add('list-group-item') liTotal.classList.add('pl-3'); ul.appendChild(liTotal); }; }; // Cast skills page async function castSkills(){ // Styling GM_addStyle(` .widget-content-area{box-shadow:0 0px 0px 0 rgba(0,0,0,0);} .widget-content-area{-webkit-box-shadow:0 0px 0px 0 rgba(0,0,0,0);} `) // Add auto-skiller link to list of skill class tabs const ulElement = document.querySelector("#simpletab"); const newLiElement = document.createElement("li"); newLiElement.setAttribute('class','nav-item'); newLiElement.innerHTML = `<a class="nav-link " href="auto_skiller" role="tab" aria-selected="false">Auto Skiller</a>`; ulElement.appendChild(newLiElement); }; // Support page alert async function support(){ if (GM_getValue("auth").match("Full")){ alert('WARNING: Submitting a support ticket will prevent MoxxiMod+ features from running until the support ticket is fully resolved by Outwar'); }; }; // Crew permissions admin panel page async function crewpermissions(){ GM_addStyle(` #content-header-row > table{width:100% !important;} #content-header-row > table > tbody > tr > td > form > table{width:100%;} #content-header-row > table > tbody > tr > td > form > table > tbody > tr > td{padding:5px;} #content-header-row > table > tbody > tr > td > form > table > tbody > tr:not(:nth-last-child(-n+2)) {border-bottom:1px SOLID #444444;} #content-header-row > table > tbody > tr > td > form > table > tbody > tr > td > font > b{font-size:14px;} `) document.querySelector("#content-header-row > div").remove(); document.querySelector("#content-header-row > table > tbody > tr > td > form > table > tbody > tr:nth-child(1)").remove(); const weirdTableBulbs = document.querySelectorAll("#content-header-row > table > tbody > tr > td > form > table > tbody > tr > td > img"); const randomTableBrs = document.querySelectorAll("#content-header-row > table > tbody > tr > td > form > table > tbody > tr > td > br"); weirdTableBulbs.forEach(i => { i.remove(); }); randomTableBrs.forEach(i => { i.remove(); }); const table = document.querySelector("#content-header-row > table > tbody > tr > td > form > table"); table.classList.add('widget') // Add new column for (let i = 1; i < table.rows.length - 1; i++) { let row = table.rows[i]; let newCell = row.insertCell(-1); newCell.innerHTML = "ALL"; newCell.setAttribute('style','cursor:pointer;') newCell.addEventListener('click', function(){ const boxes = row.querySelectorAll("input[type=checkbox]"); const allChecked = Array.from(boxes).every(box => box.checked); boxes.forEach(box => { box.checked = !allChecked; }); }); }; // Add new row var newRow = table.insertRow(table.rows.length - 1); for (let i = 0; i < table.rows[0].cells.length; i++) { var newCell = newRow.insertCell(i); if (i >= 2){ newCell.setAttribute('style','text-align:center;cursor:pointer;') newCell.innerHTML = 'ALL'; newCell.addEventListener('click', function() { // Collect all checkboxes in the current column let columnIndex = i; let checkboxes = []; for (let j = 1; j < table.rows.length - 1; j++) { let checkbox = table.rows[j].cells[columnIndex].querySelector("input[type=checkbox]"); if (checkbox) { checkboxes.push(checkbox); } } // Check if all checkboxes are checked const allChecked = checkboxes.every(box => box.checked); // Toggle the checked state of all checkboxes checkboxes.forEach(box => { box.checked = !allChecked; }); }); }; }; // Move button to the right var lastRow = table.rows[table.rows.length - 1]; var lastCell = lastRow.cells[0]; lastCell.setAttribute('style','text-align:left;padding:10px;') const button = document.querySelector('input[name="setperm"]') button.classList.add('btn-mm'); }; // Boot members page async function bootmem(){ // Parse table const rows = document.querySelectorAll('.table.table-striped tbody tr'); rows.forEach(row => { const nameCell = row.querySelector('td:first-child'); // Select the first column (Name) const input = row.querySelector('input[type="checkbox"]'); // Select the input element if (nameCell && input) { const nameValue = nameCell.innerHTML; // Get name and remove spaces input.id = `BOOT${nameValue.toLowerCase()}`; // Assign ID to the input element }; }); // Create the div element const newDiv = document.createElement('div'); newDiv.style.position = 'fixed'; newDiv.style.bottom = '0'; newDiv.style.left = '0'; newDiv.style.height = '40px'; newDiv.style.width = '100%'; newDiv.style.zIndex = '1000'; newDiv.innerHTML = ` <div class="widget" style="padding:5px;"> <p style="text-align:center;"> <input type="text" class="form-control-new" placeholder="Select by name..." style="width:100%;" id="bootByName"></input> </p> </div> `; document.body.appendChild(newDiv); // Function document.querySelector("#bootByName").addEventListener('input', async () => { const bootList = document.querySelector("#bootByName").value.replace(/ /g,','); const bootArray = bootList.split(','); for (let char of bootArray){ const inputBox = document.querySelector(`#BOOT${char.toLowerCase()}`) if (inputBox){ inputBox.checked = true; }; }; }); }; // Forward to oracle message after activating an oracle async function oracle(){ if (document.body.innerHTML.match(/You may view the Oracle's prediction in your message center/i)){ document.querySelector("#content-header-row").innerHTML = `<img src="https://studiomoxxi.com/moxximod/loading-gif.gif" height="80px" width="80px">` const messages = await superfetch('ow_messagecenter'); const message = messages.match(/view_ow_message\.php\?id=[0-9]+/i); window.location.href = message; } else { return; }; }; async function moxxivision(server,serverNo,rgaName,charId){ // Open blank overlay await blankOverlay(server,serverNo,rgaName,charId); const closeLink = document.body.innerHTML.match(/<a href="([^"]*)">CLOSE<\/a>/i)[1]; // Root page styling GM_addStyle(` #overlayWidget{background-image: url("https://studiomoxxi.com/moxximod/claptrap_loading.webp") !important;background-size: 100% 100% !important;background-attachment: fixed !important;background-position: center !important;background-repeat: no-repeat !important;} #blankOverlay{background-color:#181818 !important;} h1{font-family:VT323,monospace;letter-spacing:6px;margin-bottom:1rem;} #mvRgaSelectDiv{height:300px;overflow-y:auto;overflow-x:hidden;width:600px;border:1px solid #ffffff;background:#000000;} .mv-rga-select-table > tbody > tr:hover{transition: 0.1s ease-out;background-color:#0C0C0C;} .mv-rga-select-table th, .mv-rga-select-table td {vertical-align:middle;font-family:VT323,monospace;padding: 8px 10px;font-size:14px;cursor:pointer;color:#FFFFFF;} .mv-rga-select-table{width:600px;} .rga-selected{transition: 0.1s ease-out;background-color:#181818 !important;} `); // Moxxivision styling GM_addStyle(` #moxxivision{width:100%;height:100%;background:#181818;position:fixed;top:0px;left:0px;z-index:100;} div.mv-table-container{text-align:left;display:inline-block;box-shadow: 0 0 10px rgba(0, 0, 0, 1);padding:10px;background-color:#0C0C0C;} .mv-table > thead > tr > th{text-transform: uppercase;padding:5px;background-color:#1e1e1e;color:#ffffff;font-weight:100;border:1px solid #000000;} .mv-table > tbody > tr > td{padding:5px;color:#ffffff;border:1px solid #000000;} .mv-table > tbody > tr > td > img{height:35px;width:35px;border-radius:5px;margin:3px;background-color:#000000;} .mv-table > thead > tr > th > img{height:30px;width:30px;border-radius:5px;} .mv-table > tbody > tr:nth-child(even){background-color:#1e1e1e;} .mv-table > tbody > tr:nth-child(odd){background-color:#2d2d2d;} td.mv-augs > img {width:15px !important;height:15px !important;} img.mv-augs-big{width:20px !important;height:20px !important;} #mvContent{position:absolute;transition: top 0.5s ease;width:100%;text-align:center;overflow:auto;height:90%;display:inline-block;box-shadow: 0 0 10px rgba(0, 0, 0, 1);padding:10px;background-color:#0C0C0C;} .row1{text-align:center;position:fixed;top:0px;width:100%;z-index:101;background:#0C0C0C;padding:5px;box-shadow: 0 0 5px rgba(0, 0, 0, 1);} .row2{text-align:center;position:fixed;top:-200px;width:100%;z-index:102;background:#0C0C0C;padding:5px;transition: top 0.5s ease;box-shadow: 0 0 5px rgba(0, 0, 0, 1);} .row2-container{max-width:1200px;margin: 0 auto;} #loadingDiv{padding:100px;font-size:62px;color:#ffffff;font-family:Montserrat,sans-serif;letter-spacing:10px;position:fixed;top:0px;text-align:center;height:100%;width:100%;z-index:201;background-image: url("https://studiomoxxi.com/moxximod/claptrap_loading.webp");background-size: 100% 100%;background-attachment: fixed;background-position:center;background-repeat:no-repeat;} .mv-btn{letter-spacing:3px;margin:2px;font-size:14px;display:inline-block;font-family:"VT323",monospace;border: 1px solid white;color:#ffffff;padding: 2px 4px;width:200px;text-transform:uppercase;cursor:pointer;text-align:center;transition: top 1s ease;} .mv-btn-sub{letter-spacing:3px;margin:2px;font-size:14px;display:inline-block;font-family:"VT323",monospace;border: 1px solid white;color:#ffffff;padding: 1px 2px;width:50px;text-transform:uppercase;cursor:pointer;text-align:center;transition: top 1s ease;} .mv-btn:hover,.mv-btn-sub:hover{animation: btn-hover 0.5s ease forwards;} @keyframes btn-hover {0%{filter:blur(0px);background-color:#0C0C0C;color:#ffffff;} 100%{filter:blur(1px);background-color:#0C0C0C;color:#ffffff;}} .mv-filter{background-color: #0C0C0C;color: #ffffff;border: 1px solid #ffffff;margin-bottom: 0.5rem;width: 100%;font-family: VT323,monospace;font-size: 16px;padding: 7px;} .blank-cell{background-color:#0C0C0C !important;color:#0C0C0C !important;border:0px !important;} span.blink{animation: blink 0.5s infinite;} @keyframes blink{50%{opacity:0;}} div.aug-data-div {display:inline-block;margin:3px;text-align:center;background: #000000;padding-left:5px;padding-right:5px;padding-top:5px;border-radius:4px;border: 1px SOLID #000000;box-shadow: 0 0 2px rgba(0, 0, 0, 1);} img.mv-item{margin:3px;background: #000000;border-radius:4px;border: 1px SOLID #000000;box-shadow: 0 0 3px rgba(0, 0, 0, 1);height:30px;width:30px;} `) // RGA-selection page HTML document.querySelector("#overlayWidget").innerHTML = ` <center> <h1 style="border:0px SOLID #ffffff;font-size:72px;padding:10px;color:#ffffff;letter-spacing:10px;font-weight:100;font-family:Montserrat,sans-serif;">MOXXIVISION v4</h1> <div id="moxxivisionLoadDiv"> <div style="width:600px;text-align:left;"> <a href="javascript:void(0);" id="mvSelectAll">select all</a> / <a href="javascript:void(0);" id="mvDeselectAll">deselect all</a> </div> <div id="mvRgaSelectDiv"> </div><br> <a href="javascript:void(0);" style="width:300px;background-color:#000000;padding:15px;" class="mv-btn" id="mvStart">// Load greatness</span><span class="text-decoration">_</span><span class="decoration">⇒</a> </a> </div> ` // Select all and deselect all links document.querySelector("#mvSelectAll").addEventListener('click', async function(){ document.querySelectorAll("#mvRgaSelectDiv > table > tbody tr").forEach(tr => { tr.classList.add("rga-selected"); }); }); document.querySelector("#mvDeselectAll").addEventListener('click', async function(){ document.querySelectorAll("#mvRgaSelectDiv > table > tbody tr").forEach(tr => { tr.classList.remove("rga-selected"); }); }); // Moxxivision load data array const rgas = []; // If no sessions are saved if (GM_getValue('savedRgas') == "no sessions saved" || !GM_getValue('auth').match('Full')){ document.querySelector("#moxxivisionLoadDiv").innerHTML = `<img src="https://studiomoxxi.com/moxximod/loading-gif.gif" height="80px" width="80px">` const ajax = await superfetch(`ajax/accounts.php?t_serv=${serverNo}`); const charIds = ajax.match(/"id":"[0-9]+"/g).map(i => i.match(/[0-9]+/)); const buildLoadDataArray = async (id) => { const profileData = await superfetchProfile(`profile?id=${id}`); const character = profileData.name; const level = profileData.level; rgas.push([`rg_sess_id=${rgaName}&suid=${id}&serverid=${serverNo}`,'--',id.toString(),character,level]); }; await Promise.all(charIds.map(buildLoadDataArray)); GM_addStyle(`.mv-rga-cell{display:none;}`) await mv(rgas); // If sessions are saved } else { const array = GM_getValue('savedRgas').split('><'); const rows = []; const tableRows = async (id) => { const rga = id.match(/>(.*?)</i)[1]; const sess = id.match(/rg_sess_id=([A-Za-z0-9]+)/i)[1]; rows.push(`<tr><td>${rga}</td><td id="sess">${sess}</td></tr>`); }; await Promise.all(array.map(tableRows)); document.querySelector("#mvRgaSelectDiv").innerHTML = ` <table class="mv-rga-select-table"> <thead><tr><th>RGA</th><th>SESSION</th></tr></thead> <tbody>${rows.join('')}</tbody> </table> ` // 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 = '<span class="blink">⧖</span>' rows.forEach(tr => { array.push(tr.innerHTML.match(/<td id="sess">(.*?)<\/td>/i)[1]) }); const get = await mmplus(`IDGrab|rganame|${server}|${array.join(',')}`); const parse = get.match(/<tr><td>.*?<\/td><td>.*?<\/td><td>.*?<\/td><td>.*?<\/td><\/tr>/g); parse.forEach(async function(i){ const string = i.match(/<tr><td>(.*?)<\/td><td>(.*?)<\/td><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 = ` <div id="moxxivision"> <!-- Header buttons --> <div class="row1" id="mvHeader"> <a href="javascript:void(0);" class="mv-btn open-btn" alt="character">CHARACTER</a> <a href="javascript:void(0);" class="mv-btn" id="equipment-equipped">EQUIPPED</a> <a href="javascript:void(0);" class="mv-btn open-btn" alt="equipment">SLOTS</a> <a href="javascript:void(0);" class="mv-btn open-btn" alt="storage">STORAGE</a> <a href="javascript:void(0);" class="mv-btn open-btn" alt="plus">PLUS TABS</a> <a href="${closeLink}" class="mv-btn">EXIT</a> </div> <!-- Character buttons --> <div class="row2" id="div-character" style="text-align:center"> <div class="row2-container"> <a href="javascript:void(0);" class="mv-btn close-btn" id="character-overview">OVERVIEW</a> <a href="javascript:void(0);" class="mv-btn close-btn" id="character-stats">STATS</a> <a href="javascript:void(0);" class="mv-btn close-btn" id="character-factions">FACTIONS</a> <a href="javascript:void(0);" class="mv-btn close-btn" id="character-collections">COLLECTIONS</a> <a href="javascript:void(0);" class="mv-btn close-btn" id="character-skills">SKILLS</a> <a href="javascript:void(0);" class="mv-btn close-btn" id="character-underlings">UNDERLINGS</a> <a href="javascript:void(0);" class="mv-btn close-btn" id="character-god-slayer">GOD SLAYER</a> <a href="javascript:void(0);" class="mv-btn close-btn" id="character-codex">CODEX</a> <a href="javascript:void(0);" class="mv-btn close-btn">CLOSE</a> </div> </div> <!-- Equipment buttons --> <div class="row2" id="div-equipment" style="text-align:center"> <div class="row2-container"> <a href="javascript:void(0);" class="mv-btn eq-slot close-btn" id="equipment-10">CORE</a> <a href="javascript:void(0);" class="mv-btn eq-slot close-btn" id="equipment-5">HEAD</a> <a href="javascript:void(0);" class="mv-btn eq-slot close-btn" id="equipment-6">NECK</a> <a href="javascript:void(0);" class="mv-btn eq-slot close-btn" id="equipment-3">WEAPON</a> <a href="javascript:void(0);" class="mv-btn eq-slot close-btn" id="equipment-0">BODY</a> <a href="javascript:void(0);" class="mv-btn eq-slot close-btn" id="equipment-1">SHIELD</a> <a href="javascript:void(0);" class="mv-btn eq-slot close-btn" id="equipment-7">BELT</a> <a href="javascript:void(0);" class="mv-btn eq-slot close-btn" id="equipment-9">PANTS</a> <a href="javascript:void(0);" class="mv-btn eq-slot close-btn" id="equipment-4">RING</a> <a href="javascript:void(0);" class="mv-btn eq-slot close-btn" id="equipment-2">FOOT</a> <a href="javascript:void(0);" class="mv-btn close-btn" id="equipment-chaos-gem">CHAOS GEM</a> <a href="javascript:void(0);" class="mv-btn close-btn" id="equipment-badge">BADGE</a> <a href="javascript:void(0);" class="mv-btn close-btn" id="equipment-orbs">ORBS</a> <a href="javascript:void(0);" class="mv-btn close-btn" id="equipment-rune">RUNE</a> <a href="javascript:void(0);" class="mv-btn close-btn" id="equipment-booster">BOOSTER</a> <a href="javascript:void(0);" class="mv-btn close-btn" id="equipment-crests">CRESTS</a> <a href="javascript:void(0);" class="mv-btn close-btn" id="equipment-augments">AUGMENTS</a> <a href="javascript:void(0);" class="mv-btn close-btn">CLOSE</a> </div> </div> <!-- Storage buttons --> <div class="row2" id="div-storage" style="text-align:center"> <div class="row2-container"> <a href="javascript:void(0);" class="mv-btn close-btn" id="storage-backpack">BACKPACK</a> <a href="javascript:void(0);" class="mv-btn bp-tab close-btn" id="storage-quest-items" alt="quest">QUEST ITEMS</a> <a href="javascript:void(0);" class="mv-btn bp-tab close-btn" id="storage-orbs" alt="orb">ORBS</a> <a href="javascript:void(0);" class="mv-btn bp-tab close-btn" id="storage-potions" alt="potion">POTIONS</a> <a href="javascript:void(0);" class="mv-btn bp-tab close-btn" id="storage-keys" alt="key">KEYS</a> <a href="javascript:void(0);" class="mv-btn close-btn" id="storage-equipment">EQUIPMENT</a> <a href="javascript:void(0);" class="mv-btn close-btn" id="storage-vault">VAULT</a> <a href="javascript:void(0);" class="mv-btn close-btn" id="storage-augments">AUGMENTS</a> <a href="javascript:void(0);" class="mv-btn close-btn">CLOSE</a> </div> </div> <!-- Special view buttons --> <div class="row2" id="div-plus" style="text-align:center"> <div class="row2-container"> <a href="javascript:void(0);" class="mv-btn mv-plus open-btn close-btn" id="plus-events" alt="events">EVENT MANAGER</a> <a href="javascript:void(0);" class="mv-btn mv-plus close-btn" id="plus-item-slotting" alt="slotting">ITEM SLOTTING</a> <a href="javascript:void(0);" class="mv-btn mv-plus close-btn" id="plus-augment-data" alt="slotting">AUGMENT DATA</a> <a href="javascript:void(0);" class="mv-btn mv-plus close-btn" id="plus-max-rage">MR OPTIMIZER</a> <a href="javascript:void(0);" class="mv-btn close-btn">CLOSE</a> </div> </div> <!-- Events view buttons --> <div class="row2" id="div-events" style="text-align:center"> <div class="row2-container"> <a href="javascript:void(0);" class="mv-btn woz-top close-btn" id="plus-events-war-of-zhul">WAR OF ZHUL</a> <a href="javascript:void(0);" class="mv-btn woz-top close-btn" id="plus-events-trial-of-power">TRIAL OF POWER</a> <a href="javascript:void(0);" class="mv-btn close-btn" id="plus-events-halloween">HALLOWEEN</a> <a href="javascript:void(0);" class="mv-btn close-btn" id="plus-events-christmas">CHRISTMAS</a> <a href="javascript:void(0);" class="mv-btn close-btn">CLOSE</a> </div> </div> <!-- Content --> <div id="mvContent"> <span style="border:0px SOLID #ffffff;font-size:62px;position:relative;top:100px;color:#ffffff;letter-spacing:10px;font-weight:100;font-family:Montserrat,sans-serif;"> <span class="blink">//</span> MOXXIVISION v_4 </span> </div> <!-- End --> </div> ` // 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(/<imgborder="0"src="images\/suppliestriangle\.gif"width="11"height="11">([0-9]+)%<\/td>/i)[1]) const gold = i.profile.gold.toLocaleString(); rows.push(`<tr>${i.static}<td>${charclass}</td><td>${crew}</td><td>${rage}</td><td>${exp}</td><td>${tolvl}</td><td>${today}</td><td>${yesterday}</td><td>${strength}</td><td>${supplies}</td><td>${gold}</td></tr>`); // 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(/<imgborder="0"src="images\/suppliestriangle\.gif"width="11"height="11">([0-9]+)%<\/td>/i)[1]) }; await Promise.all(array.map(table)); const avgSupplies = (totSupplies/array.length).toFixed(2); // Build HTML document.querySelector("#mvContent").innerHTML = ` <div class="mv-table-container"> <table class="mv-table" id="mvTable"> <thead><tr> <th style="width:200px;font-weight:900;">TOTAL POWER</th> <th style="width:200px;font-weight:900;">TOTAL ELEMENTAL</th> <th style="width:200px;font-weight:900;">TOTAL CHAOS</th> <th style="width:200px;font-weight:900;">TOTAL RPT</th> <th style="width:200px;font-weight:900;">TOTAL MAX RAGE</th> <th style="width:200px;font-weight:900;">SUPPLIES (<a href="javascript:void(0)" id="maxAllSuppliesLink">MAX ALL</a>)</th> </tr></thead> <tbody><tr> <td>${totPower.toLocaleString()}</td> <td>${totEle.toLocaleString()}</td> <td>${totChaos.toLocaleString()}</td> <td>${totRpt.toLocaleString()}</td> <td>${totMr.toLocaleString()}</td> <td><span id="avgSupplies">${avgSupplies}%</span></td> </tr></thead> </table> </div> <p> <div class="mv-table-container"> <table class="mv-table sortable" id="mvTable"> <thead><tr><th>char</th><th></th><th class="mv-rga-cell">rga</th><th>lvl</th><th>class</th><th>crew</th><th>rage</th><th>experience</th><th>to level</th><th>growth today</th><th>yesterday</th><th>strength</th><th>supplies</th><th>gold</th></tr></thead> <tbody>${rows.join('')}</tbody> </table></div> ` // Max all supplies function document.querySelector("#maxAllSuppliesLink").addEventListener('click', async function(){ // Loading const loadingDiv = document.createElement("div"); loadingDiv.innerHTML = `loading<br><span class="blink">⧖</span>` 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(`<tr> ${i.static} <td>${mr.toLocaleString()}</td> <td>${power}</td> <td>${ele}</td> <td>${atk}</td> <td>${hp}</td> <td>${chaos}</td> <td><font color="#00FFFF">${res1}</td> <td><font color="#FFFF00">${res2}</td> <td><font color="#7e01bc">${res3}</td> <td><font color="#FF0000">${res4}</td> <td><font color="#00FF00">${res5}</td> <td>${rpt.toLocaleString()}</td> <td>${mrrptratio.toFixed(1)}</td> <td>${ept}</td> <td>${wilderness}</td> </tr>`); }; await Promise.all(array.map(table)); document.querySelector("#mvContent").innerHTML = ` <div class="mv-table-container"><table class="mv-table sortable" id="mvTable"> <thead><tr><th>char</th><th></th><th class="mv-rga-cell">rga</th><th>lvl</th><th>max rage</th><th>power</th><th>ele</th><th>atk</th><th>hp</th><th>chaos</th><th>res</th><th>res</th><th>res</th><th>res</th><th>res</th><th>rpt</th><th>mr:rpt</th><th>ept</th><th>wilderness</th></tr></thead> <tbody>${rows.join('')}</tbody> </table></div> ` 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(`<tr> ${i.static} <td>${faction}</td> <td>${a}</td> <td>${d}</td> <td>${v}</td> <td>${t}</td> <td>${c}</td> </tr>`); }; await Promise.all(array.map(table)); document.querySelector("#mvContent").innerHTML = ` <div class="mv-table-container"><table class="mv-table sortable" id="mvTable"> <thead><tr><th>char</th><th></th><th class="mv-rga-cell">rga</th><th>lvl</th><th>current faction</th><th>alvar loyalty</th><th>delruk loyalty</th><th>vordyn loyalty</th><th>total loyalty</th><th>codex level</th></tr></thead> <tbody>${rows.join('')}</tbody> </table></div> ` 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(`<tr>${i.static}<td>${skillclass}</td><td>${skillpoints}</td><td>${wall}</td><td>${slayer}</td><td>${grind}</td><td>${influence}</td><td style="max-width:750px;">${active.join('')}</td></tr>`); }; await Promise.all(array.map(table)); document.querySelector("#mvContent").innerHTML = ` <div class="mv-table-container"><table class="mv-table sortable" id="mvTable"> <thead><tr><th>char</th><th></th><th class="mv-rga-cell">rga</th><th>lvl</th><th>class</th><th>skill points</th><th><img src="images/skills/shieldwall.png"></th><th><img src="images/skills/GodSlayerSkill.png"></th><th><img src="images/skills/dailygrind.png"></th><th><img src="images/skills/influenceskill.png"></th><th>active skills</th></tr></thead> <tbody>${rows.join('')}</tbody> </table></div> ` 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(`<tr>${i.static}<td>${lings}</td><td>${allLingsExp.toLocaleString()}</td><td>${allLingsPower.toLocaleString()}</td><td>${loyalty}</td><td>${maxAtk}</td><td>${maxHp}</td></tr>`); }; await Promise.all(array.map(table)); document.querySelector("#mvContent").innerHTML = ` <div class="mv-table-container"><table class="mv-table sortable" id="mvTable"> <thead><tr><th>char</th><th></th><th class="mv-rga-cell">rga</th><th>lvl</th><th>lings</th><th>total experience</th><th>total power</th><th>loyalty enhancement</th><th>ATK % MAXED BUFF</th></th><th>HP % MAXED BUFF</th></tr></thead> <tbody>${rows.join('')}</tbody> </table></div> ` 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(`<tr>${i.static}<td>${crew}</td><td>${i.profile.godslayer}</td><td class="filt" style="max-width:750px;">${list.join(', ')}</td></tr>`); }; await Promise.all(array.map(table)); document.querySelector("#mvContent").innerHTML = ` <div class="mv-table-container"> <input type="text" id="filter" class="mv-filter" placeholder="Filter missing gods..."><br> <table class="mv-table sortable filterable" id="mvTable"> <thead><tr><th>char</th><th></th><th class="mv-rga-cell">rga</th><th>lvl</th><th>crew</th><th>slayer lvl</th><th>gods missing</th></tr></thead> <tbody>${rows.join('')}</tbody> </table></div> ` 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(`<tr>${i.static}<td>${anjou}</td><td>${reikar}</td><td>${lorren}</td><td>${lucile}</td><td>${weima}</td><td>${souma}</td><td>${vanisha}</td><td>${drolba}</td><td>${quibel}</td></tr>`); }; await Promise.all(array.map(table)); document.querySelector("#mvContent").innerHTML = ` <div class="mv-table-container"><table class="mv-table sortable" id="mvTable"> <thead><tr><th>char</th><th></th><th class="mv-rga-cell">rga</th><th>lvl</th><th>anjou</th><th>reikar</th><th>lorren</th><th>lucile</th><th>weima</th><th>souma</th><th>vanisha</th><th>drolba</th><th>quibel</th></tr></thead> <tbody>${rows.join('')}</tbody> </table></div> ` 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]+<br>/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(`<tr>${i.static}<td>${completed.length}</td><td class="filt">${incomplete.join(',')}</td></tr>`); }; await Promise.all(array.map(table)); document.querySelector("#mvContent").innerHTML = ` <div class="mv-table-container"> <input type="text" id="filter" class="mv-filter" placeholder="Filter missing codex..."><br> <table class="mv-table sortable filterable" id="mvTable"> <thead><tr><th>char</th><th></th><th class="mv-rga-cell">rga</th><th>lvl</th><th>influence</th><th>incomplete codex</th></tr></thead> <tbody>${rows.join('')}</tbody> </table></div> ` 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 = `<img src="${i.profile.core.img}" onmouseover="itempopup(event,'${i.profile.core.id}')" onmouseout="kill()" style="${slotsize}">` const head = `<img src="${i.profile.head.img}" onmouseover="itempopup(event,'${i.profile.head.id}')" onmouseout="kill()" style="${slotsize}">` const neck = `<img src="${i.profile.neck.img}" onmouseover="itempopup(event,'${i.profile.neck.id}')" onmouseout="kill()" style="${slotsize}">` const weapon = `<img src="${i.profile.weapon.img}" onmouseover="itempopup(event,'${i.profile.weapon.id}')" onmouseout="kill()" style="${slotsize}">` const body = `<img src="${i.profile.body.img}" onmouseover="itempopup(event,'${i.profile.body.id}')" onmouseout="kill()" style="${slotsize}">` const shield = `<img src="${i.profile.shield.img}" onmouseover="itempopup(event,'${i.profile.shield.id}')" onmouseout="kill()" style="${slotsize}">` const belt = `<img src="${i.profile.belt.img}" onmouseover="itempopup(event,'${i.profile.belt.id}')" onmouseout="kill()" style="${slotsize}">` const pants = `<img src="${i.profile.pants.img}" onmouseover="itempopup(event,'${i.profile.pants.id}')" onmouseout="kill()" style="${slotsize}">` const ring = `<img src="${i.profile.ring.img}" onmouseover="itempopup(event,'${i.profile.ring.id}')" onmouseout="kill()" style="${slotsize}">` const foot = `<img src="${i.profile.foot.img}" onmouseover="itempopup(event,'${i.profile.foot.id}')" onmouseout="kill()" style="${slotsize}">` const chaosgem = `<img src="${i.profile.gem.img}" onmouseover="itempopup(event,'${i.profile.gem.id}')" onmouseout="kill()" style="${slotsize}">` const badge = `<img src="${i.profile.badge.img}" onmouseover="itempopup(event,'${i.profile.badge.id}')" onmouseout="kill()" style="${slotsize}">` const rune = `<img src="${i.profile.rune.img}" onmouseover="itempopup(event,'${i.profile.rune.id}')" onmouseout="kill()" style="${slotsize}">` const orb1 = `<img src="${i.profile.orb1.img}" onmouseover="itempopup(event,'${i.profile.orb1.id}')" onmouseout="kill()" style="${slotsize}">` const orb2 = `<img src="${i.profile.orb2.img}" onmouseover="itempopup(event,'${i.profile.orb2.id}')" onmouseout="kill()" style="${slotsize}">` const orb3 = `<img src="${i.profile.orb3.img}" onmouseover="itempopup(event,'${i.profile.orb3.id}')" onmouseout="kill()" style="${slotsize}">` rows.push(`<tr>${i.static}<td>${power}</td><td>${ele}</td><td>${mr.toLocaleString()}</td><td>${core}</td><td>${head}</td><td>${neck}</td><td>${weapon}</td><td>${body}</td><td>${shield}</td><td>${belt}</td><td>${pants}</td><td>${ring}</td><td>${foot}</td><td>${chaosgem}</td><td>${badge}</td><td>${rune}</td><td>${orb1}</td><td>${orb2}</td><td>${orb3}</td></tr>`); }; await Promise.all(array.map(table)); document.querySelector("#mvContent").innerHTML = ` <div class="mv-table-container"> <table class="mv-table sortable" id="mvTable"> <thead><tr><th>char</th><th></th><th class="mv-rga-cell">rga</th><th>lvl</th><th>POW</th><th>ELE</th><th>MR</th><th>COR</th><th>HED</th><th>NCK</th><th>WEP</th><th>BDY</th><th>SHD</th><th>BELT</th><th>PNT</th><th>RNG</th><th>FOT</th><th>GEM</th><th>BDG</th><th>RNE</th><th>ORB</th><th>ORB</th><th>ORB</th></tr></thead> <tbody>${rows.join('').replace(/onclick="[^"]*"/g,'')}</tbody> </table></div> ` 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(` <tr> ${i.static} <td style="max-width:200px"><font color="#${itemData.rarity}">${itemData.name}</font></td> <td><img src="${itemData.img}" onmouseover="itempopup(event,'${itemid}')" onmouseout="kill()" style="height:35px;width:35px"></td> <td style="max-width:75px" class="mv-augs">${itemData.augs}</td> <td>${itemData.gems}</td> <td>${itemData.atk.toLocaleString()}</td> <td>${itemData.hp.toLocaleString()}</td> <td>${itemData.ele.toLocaleString()}</td> <td>${itemData.chaosdmg}</td> <td>${itemData.resist}</td> <td>${itemData.chaosres}</td> <td>${itemData.vile}</td> <td>${itemData.rpt.toLocaleString()}</td> <td>${itemData.ept.toLocaleString()}</td> <td>${itemData.rampage}%</td> <td>${itemData.critical}%</td> <td>${itemData.maxrage.toLocaleString()}</td> <td>${itemData.block}%</td> <td>${itemData.eleblock}%</td> <td>${itemData.ps}</td> </tr> `); }; await Promise.all(array.map(table)); document.querySelector("#mvContent").innerHTML = ` <div class="mv-table-container"> <table class="mv-table sortable" id="mvTable"> <thead><tr><th>char</th><th></th><th class="mv-rga-cell">rga</th><th>lvl</th><th>name</th><th>img</th><th>augs</th><th>gem</th><th>atk</th><th>hp</th><th>ele</th><th><font color="#f441be">chs</th><th>res</th><th><font color="#f441be">res</th><th>vle</th><th>rpt</th><th>ept</th><th>rmp</th><th>crt</th><th>mr</th><th>blk</th><th><font color="#00FF00">blk</th><th>ps</th></tr></thead> <tbody>${rows.join('')}</tbody> </table></div> ` 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(` <tr> ${i.static} <td style="max-width:200px"><font color="#${itemData.rarity}">${itemData.name}</font></td> <td><img src="${itemData.img}" onmouseover="itempopup(event,'${itemid}')" onmouseout="kill()" style="height:35px;width:35px"></td> <td>${itemData.chaosdmg.toLocaleString()}</td> <td>${itemData.rampage}%</td> <td>${itemData.critical}%</td> <td>${itemData.maxrage.toLocaleString()}</td> </tr> `); }; await Promise.all(array.map(table)); document.querySelector("#mvContent").innerHTML = ` <div class="mv-table-container"> <table class="mv-table sortable" id="mvTable"> <thead><tr><th>char</th><th></th><th class="mv-rga-cell">rga</th><th>lvl</th><th>name</th><th>img</th><th>chaos</th><th>rampage</th><th>critical</th><th>max rage</th></tr></thead> <tbody>${rows.join('')}</tbody> </table></div> ` 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(` <tr> ${i.static} <td style="max-width:200px"><font color="#${itemData.rarity}">${itemData.name}</font></td> <td><img src="${itemData.img}" onmouseover="itempopup(event,'${itemid}')" onmouseout="kill()" style="height:35px;width:35px"></td> <td>${itemData.atk.toLocaleString()}</td> <td>${itemData.hp.toLocaleString()}</td> <td>${itemData.ele.toLocaleString()}</td> </tr> `); }; await Promise.all(array.map(table)); document.querySelector("#mvContent").innerHTML = ` <div class="mv-table-container"> <table class="mv-table sortable" id="mvTable"> <thead><tr><th>char</th><th></th><th class="mv-rga-cell">rga</th><th>lvl</th><th>name</th><th>img</th><th>atk</th><th>hp</th><th>elemental</th></tr></thead> <tbody>${rows.join('')}</tbody> </table></div> ` 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(` <tr> ${i.static} <td><img src="${orb1Data.img}" onmouseover="itempopup(event,'${orb1id}')" onmouseout="kill()" style="height:35px;width:35px"></td> <td><img src="${orb2Data.img}" onmouseover="itempopup(event,'${orb2id}')" onmouseout="kill()" style="height:35px;width:35px"></td> <td><img src="${orb3Data.img}" onmouseover="itempopup(event,'${orb3id}')" onmouseout="kill()" style="height:35px;width:35px"></td> <td><font color="#${orb1Data.rarity}">${orb1Data.name}</font></td> <td><font color="#${orb2Data.rarity}">${orb2Data.name}</font></td> <td><font color="#${orb3Data.rarity}">${orb3Data.name}</font></td> <td>${(orb1Data.maxrage + orb2Data.maxrage + orb3Data.maxrage).toLocaleString()}</td> <td>${(orb1Data.ele + orb2Data.ele + orb3Data.ele).toLocaleString()}</td> <td>${(orb1Data.atk + orb2Data.atk + orb3Data.atk).toLocaleString()}</td> <td>${(orb1Data.hp + orb2Data.hp + orb3Data.hp).toLocaleString()}</td> <td>${(orb1Data.rpt + orb2Data.rpt + orb3Data.rpt).toLocaleString()}</td> <td>${(orb1Data.ept + orb2Data.ept + orb3Data.ept).toLocaleString()}</td> <td>${(orb1Data.chaosdmg + orb2Data.chaosdmg + orb3Data.chaosdmg).toLocaleString()}</td> </tr> `); }; await Promise.all(array.map(table)); document.querySelector("#mvContent").innerHTML = ` <div class="mv-table-container"> <table class="mv-table sortable" id="mvTable"> <thead><tr><th>char</th><th></th><th class="mv-rga-cell">rga</th><th>lvl</th><th>img</th><th>img</th><th>img</th><th>orb 1</th><th>orb 2</th><th>orb 3</th><th>mr</th><th>ele</th><th>atk</th><th>hp</th><th>rpt</th><th>ept</th><th>chaos</th></tr></thead> <tbody>${rows.join('')}</tbody> </table></div> ` 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(` <tr> ${i.static} <td style="max-width:200px"><font color="#${itemData.rarity}">${itemData.name}</font></td> <td><img src="${itemData.img}" onmouseover="itempopup(event,'${itemid}')" onmouseout="kill()" style="height:35px;width:35px"></td> <td>${itemData.ele.toLocaleString()}</td> <td>${fusers}</td> </tr> `); }; await Promise.all(array.map(table)); document.querySelector("#mvContent").innerHTML = ` <div class="mv-table-container"> <table class="mv-table sortable" id="mvTable"> <thead><tr><th>char</th><th></th><th class="mv-rga-cell">rga</th><th>lvl</th><th>name</th><th>img</th><th>elemental</th><th><img src="images/items/elementalfuser.jpg" onmouseover="statspopup(event,'Elemental Fusers')" onmouseout="kill()"></th></tr></thead> <tbody>${rows.join('')}</tbody> </table></div> ` 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(` <tr> ${i.static} <td style="max-width:200px"><font color="#${itemData.rarity}">${itemData.name}</font></td> <td><img src="${itemData.img}" onmouseover="itempopup(event,'${itemid}')" onmouseout="kill()" style="height:35px;width:35px"></td> </tr> `); }; await Promise.all(array.map(table)); document.querySelector("#mvContent").innerHTML = ` <div class="mv-table-container"> <table class="mv-table sortable" id="mvTable"> <thead><tr><th>char</th><th></th><th class="mv-rga-cell">rga</th><th>lvl</th><th>booster</th><th>img</th></tr></thead> <tbody>${rows.join('')}</tbody> </table></div> ` 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( `<tr> ${i.static} <td><img src="${crest1.img}" onmouseover="itempopup(event,'${i.profile.ccrest.id}')" onmouseout="kill()" style="height:35px;width:35px"></td> <td><img src="${crest3.img}" onmouseover="itempopup(event,'${i.profile.fcrest.id}')" onmouseout="kill()" style="height:35px;width:35px"></td> <td><img src="${crest2.img}" onmouseover="itempopup(event,'${i.profile.pcrest.id}')" onmouseout="kill()" style="height:35px;width:35px"></td> <td><img src="${crest4.img}" onmouseover="itempopup(event,'${i.profile.acrest.id}')" onmouseout="kill()" style="height:35px;width:35px"></td> <td style="max-width:200px"><font color="#${crest1.rarity}">${crest1.name}</font></td> <td style="max-width:200px"><font color="#${crest3.rarity}">${crest3.name}</font></td> <td style="max-width:200px"><font color="#${crest2.rarity}">${crest2.name}</font></td> <td style="max-width:200px"><font color="#${crest4.rarity}">${crest4.name}</font></td> </tr>` ); }; await Promise.all(array.map(table)); document.querySelector("#mvContent").innerHTML = ` <div class="mv-table-container"> <table class="mv-table sortable" id="mvTable"> <thead><tr><th>char</th><th></th><th class="mv-rga-cell">rga</th><th>lvl</th><th>img</th><th>img</th><th>img</th><th>img</th><th>class</th><th>ferocity</th><th>preservation</th><th>affliction</th></tr></thead> <tbody>${rows.join('')}</tbody> </table></div> ` 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(`<tr>${i.static}<td>${augcount}</td><td class="mv-augs" style="max-width:615px;">${allaugs}</td></tr>`) }; await Promise.all(array.map(table)); document.querySelector("#mvContent").innerHTML = ` <div class="mv-table-container"> <table class="mv-table" id="mvTable"> <thead><tr> <th style="width:200px;font-weight:900;text-align:center;">TOTAL OPEN AUG SLOTS</th> </tr></thead> <tbody><tr> <td style="text-align:center;">${totalOpen}</td> </tr></thead> </table> </div> <p> <div class="mv-table-container"> <table class="mv-table sortable" id="mvTable"> <thead><tr><th>char</th><th></th><th class="mv-rga-cell">rga</th><th>lvl</th><th>count</th><th>slotted augments</th></tr></thead> <tbody>${rows.join('')}</tbody> </table></div> ` 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 => `<img ${i} onmouseout="kill()" class="mv-item">`) : ['Too many items to display']; rows.push(` <tr> ${i.static} <td>${bpCount}</td> <td>${bpCapacity}</td> <td>${bpOpen}</td> <td style="max-width:315px">${items.join('')}</td> </tr> `); }; await Promise.all(array.map(table)); document.querySelector("#mvContent").innerHTML = ` <div class="mv-table-container"> <table class="mv-table sortable" id="mvTable"> <thead><tr><th>char</th><th></th><th class="mv-rga-cell">rga</th><th>lvl</th><th>count</th><th>capacity</th><th>open slots</th><th>backpack items</th></tr></thead> <tbody>${rows.join('')}</tbody> </table></div> ` // 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 => `<th><img ${i.match(/src="[^"]*"/i)} onmouseover="statspopup(event,'${i.match(/alt="([^"]*)"/i)[1]}')" onmouseout="kill()"></th>`); // 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 += '<td>' + count + '</td>'; }; // Push entire table row of static data and item counts to the row array row.push('<tr>' + cells + '</tr>'); }; 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 = ` <span id="tableNoSpan"></span> <div class="mv-table-container" id="mv-table-container"> <table class="mv-table sortable" id="mvTable"> <thead><tr><th>char</th><th></th><th class="mv-rga-cell">rga</th><th>lvl</th>${headers[1].join('')}</tr></thead> <tbody>${rows[1].join('')}</tbody> </table></div></div> ` // 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(`<a href="javascript:void(0);" class="mv-btn-sub" style="margin-bottom:10px;" id="items-table-${i+1}">${i+1}</a>`) }; document.querySelector("#tableNoSpan").innerHTML = `<center>${tableLinks.join('')}</center>` // 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 = ` <table class="mv-table sortable" id="mvTable"> <thead><tr><th>char</th><th></th><th class="mv-rga-cell">rga</th><th>lvl</th>${headers[i+1].join('')}</tr></thead> <tbody>${rows[i+1].join('')}</tbody> </table> ` 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(/<h1>Manage Augments<\/h1>/i)){; const items = i.augmentequip.match(/<img style="max-width:40px;max-height:40px" border="0" src="[^"]*" onmouseover="itempopup\(event,'[0-9]+'\)" onmouseout="kill\(\)">/g) if (items){ rows.push('<tr>' + i.static + '<td style="width:650px">' + items.join('') + '</td></tr>')[1] }; } else { tableMsg = '<i>Equipment will only display for the originating RGA because security word is required to access items</i>' }; }; await Promise.all(array.map(table)); document.querySelector("#mvContent").innerHTML = ` <div class="mv-table-container"> <center><font color="#2D2D2D">${tableMsg}</font></center> <table class="mv-table sortable" id="mvTable"> <thead><tr><th>char</th><th></th><th class="mv-rga-cell">rga</th><th>lvl</th><th>all equipment</th></tr></thead> <tbody>${rows.join('')}</tbody> </table></div> ` 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 <b>[0-9]+ \/ ([0-9]+)<\/b>/i)[1]); const vaultCount = parseInt(i.vault.match(/Currently Storing <b>([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 => `<img ${m} onmouseout="kill()" class="mv-item">`); rows.push(` <tr> ${i.static} <td>${vaultCount}</td> <td>${vaultCapacity}</td> <td>${vaultOpen}</td> <td style="width:800px">${items.join('')}</td> </tr> `); }; } else { tableMsg = '<i>Vault items will only display for the originating RGA because security word is required to access items</i>' }; }; await Promise.all(array.map(table)); document.querySelector("#mvContent").innerHTML = ` <div class="mv-table-container"> <center><font color="#2D2D2D">${tableMsg}</font></center> <table class="mv-table sortable" id="mvTable"> <thead><tr><th>char</th><th></th><th class="mv-rga-cell">rga</th><th>lvl</th><th>items</th><th>capacity</th><th>open slots</th><th>vault</th></tr></thead> <tbody>${rows.join('')}</tbody> </table></div> ` 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(/<h1>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 => `<img ${m} onmouseout="kill()">`); rows.push('<tr>' + i.static + '<td style="width:650px">' + items.join('') + '</td></tr>')[1] }; } else { tableMsg = '<i>Augments will only display for the originating RGA because security word is required to access items</i>' }; }; await Promise.all(array.map(table)); document.querySelector("#mvContent").innerHTML = ` <div class="mv-table-container"> <center><font color="#2D2D2D">${tableMsg}</font></center> <table class="mv-table sortable" id="mvTable"> <thead><tr><th>char</th><th></th><th class="mv-rga-cell">rga</th><th>lvl</th><th>all augments</th></tr></thead> <tbody>${rows.join('')}</tbody> </table></div> ` await sortableTables(); document.querySelector("#loadingDiv").remove(); }; // Moxxivision plus tabs async function mvItemSlotting(rgas,string){ document.querySelector("#mvContent").innerHTML = ` <div class="mv-table-container" id="item-slotting-div"> <input type="text" id="itemLink" class="mv-filter" style="width:800px" placeholder="Link to item you are slotting..."> </div> ` // 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(`<tr> ${i.static} <td>${eleChar.toLocaleString()}</td> <td>${mrChar.toLocaleString()}</td> <td class="blank-cell"></td> <td><img src="${imgCurrent}" onmouseover="itempopup(event,'${itemid}')" onmouseout="kill()" style="height:35px;width:35px"></td> <td style="max-width:200px">${nameCurrent}</td> <td>${eleCurrent.toLocaleString()}</td> <td>${mrCurrent.toLocaleString()}</td> <td class="blank-cell"></td> <td><img src="${imgNew}" onmouseover="itempopup(event,'${itemidNew}')" onmouseout="kill()" style="height:35px;width:35px"></td> <td style="max-width:200px">${nameNew}</td> <td>${eleNew.toLocaleString()}</td> <td>${mrNew.toLocaleString()}</td> <td class="blank-cell"></td> <td>${(eleNew-eleCurrent).toLocaleString()}</td> <td>${(mrNew-mrCurrent).toLocaleString()}</td> </tr>`); }; await Promise.all(array.map(table)); document.querySelector("#item-slotting-div").innerHTML = ` <div id="banner"></div> <table class="mv-table sortable" id="mvTable"> <thead><tr> <th>character</th> <th></th> <th class="mv-rga-cell">rga</th> <th>lvl</th> <th>tot ele</th> <th>tot mr</th> <th class="blank-cell"></th> <th>img</th> <th>current item</th> <th>ele</th> <th>mr</th> <th class="blank-cell"></th> <th>img</th> <th>new item</th> <th>ele</th> <th>mr</th> <th class="blank-cell"></th> <th>ele change</th> <th>mr change</th> </tr></thead> <tbody>${rows.join('')}</tbody> ` 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(`<div class="aug-data-div"><img src="${img}" onmouseover="itempopup(event,'${id}')" onmouseout="kill()"><br>${count}</div>`) }); // Build rows rows.push(` <tr> ${i.static} <td>${augTotal}</td> <td>${augTotalEle.toLocaleString()}</td> <td>${augTotalPower.toLocaleString()}</td> <td>${augTotalMr.toLocaleString()}</td> <td>${augTotalChaos.toLocaleString()}</td> <td class="mv-augs" style="max-width:500px">${augImgArray.join('')}</td> </tr> `); }; await Promise.all(array.map(table)); document.querySelector("#mvContent").innerHTML = ` <div class="mv-table-container"> <table class="mv-table sortable" id="mvTable"> <thead><tr><th>char</th><th></th><th class="mv-rga-cell">rga</th><th>lvl</th><th>augs</th><th>augs ele</th><th>augs power</th><th>augs max rage</th><th>augs chaos</th><th>quantities</th></tr></thead> <tbody>${rows.join('')}</tbody> </table></div> ` 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(`<tr> ${i.static} <td>${charclass}</td> <td>${mr.toLocaleString()}</td> <td>${upgraded.toLocaleString()}</td> <td>${available.toLocaleString()}</td> <td>${missingGems}</td> <td>${(Math.ceil(mrFromGems) + available).toLocaleString()}</td> <td>${costToGem.toLocaleString()}</td> <td>${maxMr.toLocaleString()}</td> <td>${perPoint}</td> </tr>`); }; await Promise.all(array.map(table)); document.querySelector("#mvContent").innerHTML = ` <div id="banner"></div> <div class="mv-table-container"><table class="mv-table sortable" id="mvTable"> <thead><tr> <th>char</th> <th class="mv-rga-cell">rga</th> <th></th> <th>lvl</th> <th>class</th> <th>max rage</th> <th>char upgrades</th> <th>upgrades available</t> <th>missing gems</th> <th>max gains</th> <th>full gem cost</th> <th>mr if maxed</th> <th>mr per point</th> </tr></thead> <tbody>${rows.join('')}</tbody> </table></div> ` 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(`<tr> ${i.static} <td class="fury-count">${fury}</td> <td class="spark-count">${spark}</td> <td>${elepot}</td> <td>${kixpot}</td> <td>${amdirpot}</td> <td>${squidpot}</td> <td>${wonderlandpot}</td> <td>${qitem}</td> <td class="max-rage">${mr.toLocaleString()}</td> <td>${rpt.toLocaleString()}</td> <td class="projected-qitem">${Math.ceil(runsLeft*(mr+rpt)/20000+qitem)}</td> <td width="130px"><input type="text" class="mv-filter fury-math-input" value="${refills}" style="margin-bottom:0px;"></td> <td class="fury-math-output">${Math.ceil(refills*mr/20000) + Math.ceil(runsLeft*(mr+rpt)/20000+qitem)}</td> </tr>`); }; await Promise.all(array.map(table)); document.querySelector("#mvContent").innerHTML = ` <div id="banner"></div> <div class="mv-table-container"><table class="mv-table sortable" id="mvTable"> <thead> <th>char</th> <th></th> <th class="mv-rga-cell">rga</th> <th>lvl</th> <th><img src="images/rfury.jpg" onmouseover="popup(event,'Recharge the Fury')" onmouseout="kill()"></th> <th><img src="images/items/sfury.jpg" onmouseover="popup(event,'Spark the Fury')" onmouseout="kill()"></th> <th><img src="images/items/eleresistpotion.png" onmouseover="popup(event,'Potion of Elemental Resistance')" onmouseout="kill()"></th> <th><img src="images/potion28.jpg" onmouseover="popup(event,'Kix Potion')" onmouseout="kill()"></th> <th><img src="images/items/arelepot.jpg" onmouseover="popup(event,'Potion of Amdir')" onmouseout="kill()"></th> <th><img src="images/items/Item_SquidberryJuice.jpg" onmouseover="popup(event,'Squidberry Juice')" onmouseout="kill()"></th> <th><img src="images/items/itemz95.png" onmouseover="popup(event,'Wonderland Potion')" onmouseout="kill()"></th> <th><img src="${qItemImg}" onmouseover="popup(event,'${qItemLong}')" onmouseout="kill()"></th> <th>max rage</th> <th>rpt</th> <th>proj tot ${qItemShort}</th> <th>furys to use</th> <th>new proj ${qItemShort}</th> </thead> <tbody>${rows.join('')}</tbody> </table></div> ` // 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>.*?: <\/b>[0-9]+\/[0-9]+ killed/i)){ const mobNeeded = i.questnum.match(/<b>(.*?): <\/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]); //<th><img src="images/items/XmasCandyCane2013.jpg" onmouseover="popup(event,'Candy Cane')" onmouseout="kill()"></th> rows.push(` <tr> ${i.static} <td>${mr.toLocaleString()}</td> <td>${rpt.toLocaleString()}</td> <td>${components}</td> <td>${queststep}</td> </tr>`); }; await Promise.all(array.map(table)); document.querySelector("#mvContent").innerHTML = ` <div id="banner"></div> <div class="mv-table-container"><table class="mv-table sortable" id="mvTable"> <thead><tr> <th>char</th> <th></th> <th class="mv-rga-cell">rga</th> <th>lvl</th> <th>max rage</th> <th>rpt</th> <th>server components</th> <th>quest step</th> </tr></thead> <tbody>${rows.join('')}</tbody> </table> </div> ` 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]); //<th><img src="images/items/XmasCandyCane2013.jpg" onmouseover="popup(event,'Candy Cane')" onmouseout="kill()"></th> rows.push(` <tr> ${i.static} <td>${mr.toLocaleString()}</td> <td>${rpt.toLocaleString()}</td> <td>${components}</td> <td>${queststep}</td> </tr>`); }; await Promise.all(array.map(table)); document.querySelector("#mvContent").innerHTML = ` <div id="banner"></div> <div class="mv-table-container"><table class="mv-table sortable" id="mvTable"> <thead><tr> <th>char</th> <th></th> <th class="mv-rga-cell">rga</th> <th>lvl</th> <th>max rage</th> <th>rpt</th> <th>candy canes</th> <th>quest step</th> </tr></thead> <tbody>${rows.join('')}</tbody> </table> </div> ` 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<br><span class="blink">⧖</span>` 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 = ` <td><a href="profile?${rgas[i][0]}" target="_blank">${rgas[i][3]}</a></td> <td><input type="checkbox" name="${rgas[i][3]}" class="mv-checkbox"></td> <td class="mv-rga-cell">${rgas[i][1]}</td> <td>${rgas[i][4]}</td> ` }; // 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 = '<i class="fa fa-clipboard"></i>' 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(/<span style="color:#00FF00"> \(\+[0-9]+\)<\/span>/g,'').replace(/<span style="color:#[A-Za-z0-9]+">/g,'').replace(/,|%/g,''); const slot = (item.match(/\[Slot - (.*?)\]<br\/>/i) || ['','slot unknown'])[1].toLowerCase(); const img = itemid > 0 ? `<td><img src="${(item.match(/<img src="([^"]*)" style="border:1px solid #666666;margin:2px;">/i) || ['',''])[1]}" onmouseover="itempopup(event,'${itemid}')" onmouseout="kill()"></img></td>` : '<td></td>'; const rarity = (item.match(/text-shadow: #47462E 1px 1px 2px;color:#([A-Za-z0-9]+)/i) || ['',''])[1]; const name = `<td style="max-width:200px"><font color="#${rarity}">` + (item.match(/align="left">(.*?)<\/td>/i) || ['',''])[1].replace('td colspan="2"','a')+'</font></td>'; const cloned = item.match(/\[Cloned: .*?\]/i) ? true : false; const augs = '<td style="max-width:75px">' + (item.match(/src="[^"]*" ONMOUSEOVER="itempopup\(event'[0-9]+_[0-9]+'\)"/g) || []).map(i => `<img ${i.replace('(event','(event,')} onmouseout="kill()" class="mv-augs">`).join('') + '</td>'; const openaugs = item.match(/\/images\/augslot\.jpg/) ? item.match(/\/images\/augslot\.jpg/g).length : 0; const gems = '<td>' +(4-((item.match(/src="\/images\/gemslot2\.jpg"/g) || []).length)) + '</td>'; const atk = '<td>' + parseInt((item.match(/\+([0-9]+) ATK<br>/i) || [0,0])[1]).toLocaleString() + '</td>'; const hp = '<td>' + parseInt((item.match(/\+([0-9]+) HP<br>/i) || [0,0])[1]).toLocaleString() + '</td>'; 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 = '<td>' + (ele1 + ele2 + ele3 + ele4 + ele5).toLocaleString() + '</td>';; const ele6 = '<td>' + parseInt((item.match(/\+([0-9]+) Chaos<\/span>/i) || [0,0])[1]).toLocaleString() + '</td>'; 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 = '<td>' + (res1 + res2 + res3 + res4 + res5).toLocaleString() + '</td>';; const res6 = '<td>' + (item.match(/\+([0-9]+) Chaos Resist/i) || [0,0])[1] + '</td>'; const vile = '<td>' + parseInt((item.match(/\+([0-9]+) vile energy/i) || [0,0])[1]).toLocaleString() + '</td>'; const rpt = '<td>' + parseInt((item.match(/\+([0-9]+) rage per hr/i) || [0,0])[1]).toLocaleString() + '</td>'; const ept = '<td>' + parseInt((item.match(/\+([0-9]+) exp per hr/i) || [0,0])[1]).toLocaleString() + '</td>'; const rampage = '<td>' + (item.match(/\+([0-9]+) rampage/i) || [0,0])[1] + '</td>'; const critical = '<td>' + (item.match(/\+([0-9]+) critical hit/i) || [0,0])[1] + '</td>'; const mr = '<td>' + parseInt((item.match(/\+([0-9]+) max rage/i) || [0,0])[1]).toLocaleString() + '</td>'; const block = '<td>' + (item.match(/\+([0-9]+) block/i) || [0,0])[1] + '</td>'; const eleblock = '<td>' + (item.match(/\+([0-9]+) elemental block/i) || [0,0])[1] + '</td>'; const ps = '<td>' + (item.match(/\+(\d+(\.\d+)?) perfect strike/i) || [0,0])[1] + '</td>';; 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 = ` <div style="background:#870000;width:100%;height:80px;text-align:center;justify-content:center;align-items:center;display:flex;margin-bottom:1rem;"> <a href="https://www.patreon.com/moxximod" target="_blank" class="mv-btn" style="width:400px;padding:4px 8px;">PLEASE SUBSCRIBE TO MOXXIMOD+ TO UNLOCK THE ${tab} TAB</a> </div> ` }; // 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<img src="https://studiomoxxi.com/moxximod/toolbarbot.png" height="20px" width="20px" onmouseover="statspopup(event,'<font color=#FFFFFF><b>POTIONS NOT IN BACKPACK</b><br>${missing.join('<br>')}')" onmouseout="kill()">` } else { document.querySelector("#backpackitemct").innerHTML = `missing<img src="https://studiomoxxi.com/moxximod/toolbarbot.png" height="20px" width="20px" onmouseover="statspopup(event,'<font color=#FFFFFF>Please subscribe to MoxxiMod+ to see missing potions')" onmouseout="kill()">` }; }); }; }; }; 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(` <div id="blankOverlay"> <a href="https://${server}.outwar.com/home?rg_sess_id=${rgaName}&suid=${charId}&serverid=${serverNo}">CLOSE</a> <div class="widget" style="width:90%;height:90%;left:5%;right:5%;box-shadow: 0 6px 10px 0 rgba(0,0,0,1),0 1px 18px 0 rgba(0,0,0,1),0 3px 5px -1px rgba(0,0,0,.2);" id="overlayWidget"> </div> </div>`); // 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]','<br><div id="codexCheck"><img src="https://studiomoxxi.com/moxximod/loading-gif.gif" height="25px" width="25px"></div>') const changefaction = await superfetch(`changefaction`); const codexCheckDiv = document.querySelector("#codexCheck"); if (changefaction.match(codex) && codexCheckDiv){ document.querySelector("#codexCheck").innerHTML = `<font color="#ff0000">${codex} has already been activated on this account</font>` } else if (codexCheckDiv) { document.querySelector("#codexCheck").innerHTML = `<font color="#00ff00">${codex} has not been activated on this account</font>` } return; }; // Parse ele damages let itemHoly = parseInt((tip.innerHTML.replace(/,/g,'').match(/\+([0-9]+) <span style="[^"]*">Holy/i) || [0,0])[1]); let itemArcane = parseInt((tip.innerHTML.replace(/,/g,'').match(/\+([0-9]+) <span style="[^"]*">Arcane/i) || [0,0])[1]); let itemShadow = parseInt((tip.innerHTML.replace(/,/g,'').match(/\+([0-9]+) <span style="[^"]*">Shadow/i) || [0,0])[1]); let itemFire = parseInt((tip.innerHTML.replace(/,/g,'').match(/\+([0-9]+) <span style="[^"]*">Fire/i) || [0,0])[1]); let itemKinetic = parseInt((tip.innerHTML.replace(/,/g,'').match(/\+([0-9]+) <span style="[^"]*">Kinetic/i) || [0,0])[1]); let itemChaos = parseInt((tip.innerHTML.replace(/,/g,'').match(/\+([0-9]+) <span style="[^"]*">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]+) <span style="[^"]*">Holy/i) || [0,0])[1]); augsHoly += parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) <span style="[^"]*">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]+) <span style="[^"]*">Arcane/i) || [0,0])[1]); augsArcane += parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) <span style="[^"]*">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]+) <span style="[^"]*">Shadow/i) || [0,0])[1]); augsShadow += parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) <span style="[^"]*">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]+) <span style="[^"]*">Fire/i) || [0,0])[1]); augsFire += parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) <span style="[^"]*">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]+) <span style="[^"]*">Kinetic/i) || [0,0])[1]); augsKinetic += parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) <span style="[^"]*">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]+) <span style="[^"]*">Chaos/i) || [0,0])[1]); augsChaos += parseInt((aug.replace(/,/g,'').match(/\+([0-9]+) <span style="[^"]*">Chaos/i) || [0,0])[1]); }; await Promise.all(allAugs.map(augs)); if (augsHoly > 0){ tip.innerHTML = tip.innerHTML.replace(/  \+(.*?) <span style="[^"]*">Holy/i,`  +${itemHoly.toLocaleString()} <font color="#00FF00">(+${augsHoly})</font> Holy`) } if (augsHolyRes > 0){ tip.innerHTML = tip.innerHTML.replace(/  \+(.*?) Holy Resist/i,`  +${itemHolyRes.toLocaleString()} <font color="#00FF00">(+${augsHolyRes})</font> Holy Resist`) } if (augsArcane > 0){ tip.innerHTML = tip.innerHTML.replace(/  \+(.*?) <span style="[^"]*">Arcane/i,`  +${itemArcane.toLocaleString()} <font color="#00FF00">(+${augsArcane})</font> Arcane`) } if (augsArcaneRes > 0){ tip.innerHTML = tip.innerHTML.replace(/  \+(.*?) Arcane Resist/i,`  +${itemArcaneRes.toLocaleString()} <font color="#00FF00">(+${augsArcaneRes})</font> Arcane Resist`) } if (augsShadow > 0){ tip.innerHTML = tip.innerHTML.replace(/  \+(.*?) <span style="[^"]*">Shadow/i,`  +${itemShadow.toLocaleString()} <font color="#00FF00">(+${augsShadow})</font> Shadow`) } if (augsShadowRes > 0){ tip.innerHTML = tip.innerHTML.replace(/  \+(.*?) Shadow Resist/i,`  +${itemShadowRes.toLocaleString()} <font color="#00FF00">(+${augsShadowRes})</font> Shadow Resist`) } if (augsFire > 0){ tip.innerHTML = tip.innerHTML.replace(/  \+(.*?) <span style="[^"]*">Fire/i,`  +${itemFire.toLocaleString()} <font color="#00FF00">(+${augsFire})</font> Fire`) } if (augsFireRes > 0){ tip.innerHTML = tip.innerHTML.replace(/  \+(.*?) Fire Resist/i,`  +${itemFireRes.toLocaleString()} <font color="#00FF00">(+${augsFireRes})</font> Fire Resist`) } if (augsKinetic > 0){ tip.innerHTML = tip.innerHTML.replace(/  \+(.*?) <span style="[^"]*">Kinetic/i,`  +${itemKinetic.toLocaleString()} <font color="#00FF00">(+${augsKinetic})</font> Kinetic`) } if (augsKineticRes > 0){ tip.innerHTML = tip.innerHTML.replace(/  \+(.*?) Kinetic Resist/i,`  +${itemKineticRes.toLocaleString()} <font color="#00FF00">(+${augsKineticRes})</font> Kinetic Resist`) } if (augsChaos > 0){ tip.innerHTML = tip.innerHTML.replace(/  \+(.*?) <span style="[^"]*">Chaos/i,`  +${itemChaos.toLocaleString()} <font color="#00FF00">(+${augsChaos})</font> 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 = '<img src="https://studiomoxxi.com/moxximod/loading-gif.gif" style="height:100px;width:100px">' const rgaCharIds = document.body.innerHTML.replace(/[\n\r]/g,'').match(/<optgroup label="My Characters">.*?<\/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(`<tr><td>${charName}</td><td>${loyalty}</td><td>${power.toLocaleString()}</td><td>${ele.toLocaleString()}</td><td>${complete}</td><td>${codexLvl}</td><td><a href="trade?rg_sess_id=${rgaName}&suid=${charId}&serverid=${serverNo}&tradeWith=${id}">trade</a></td></tr>`); }; }; await Promise.all(rgaCharIds.map(rgaCodexLoop)); document.querySelector("#overlayWidget").innerHTML = ` <div style="text-align:center;height:100%;overflow:auto;"> <h3>${codex}</h3> <table class="table table-striped sortable" style="text-align:left"> <thead><tr><th>character</th><th>loyalty</th><th>power</th><th>ele</th><th>completed?</th><th>codex lvl</th><th>trade</th></tr></thead> ${data.join('')} </table> </div> ` await sortableTables(); }; newLink.innerHTML = `<i class="fa fa-star"></i> Codex Check`; menu.appendChild(newLink); }; async function augmentMenuModify(menu,tip){ const newLink = document.createElement('a'); newLink.href = "augmentequip"; newLink.innerHTML = `<i class="fa fa-star"></i> Add Augment`; menu.appendChild(newLink); }; async function absolutionMenuModify(menu,tip){ const newLink = document.createElement('a'); newLink.href = "itemtransfer?type=selectbadge"; newLink.innerHTML = `<i class="fa fa-star"></i> 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(` <div id="loadingOverlay"> <img src="https://studiomoxxi.com/moxximod/loading-gif.gif"> <div id="loadingOverlayText"></div> </div>`); // 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<br>Recharge the Fury<br>Cosmos Talisman<br>Tome of Daily Grind<br>Key to Knights Horror<br>Astral Shard<br>Quest Shard<br>Recharge Totem<br>Star Power<br>Ticket to the Mystifying Carnival<br>Containment Orb<br>Orb of the Scepter<br>Amulet Chest (50)'] // Death info } else if (request == 'Death, Reaper of Souls'){ return [290000000000,80,'Recharge Totem<br>Recharge the Fury<br>Standard Issue Neuralyzer<br>Death Talisman<br>Pirate Treasure Map<br>Key of the Elements<br>Advanced Neuralyzer<br>Trinket Items<br>Elemental Vigor Orb<br>Elemental Assault Orb<br>Elemental Defense Orb<br>Amulet Chest (50)<br>Chancellor Items<br>Spiral Gear'] // Maekrix info } else if (request == 'Maekrix, Dreaded Striker'){ return [320000000000,73,'Red Dragon Items<br>Astral Totem<br>Maekrix Talisman<br>Key to the Alsayic Ruins (Solo)<br>Juggernaut Talisman<br>Advanced Neuralyzer<br>Irthys Vigor Orb<br>Irthys Assault Orb<br>Irthys Defense Orb<br>Add Augment Slot<br>Remove Augment<br>Amulet Chest (50)<br>Nobel Gear'] // Blackhand info } else if (request == 'Blackhand Reborn'){ return [570000000000,61,'Augment of the Reborn Knight<br>Core of Blackhand<br>Essence of Reincarnation<br>Blackhand Talisman<br>Profound Ward<br>8-Bit Banana<br>Buckler of Insanity<br>Hauberk of Lunacy<br>Charm of Havoc<br>Unstoppable Concoction<br>Advanced Neuralyzer<br>Power Potion Pack<br>Flask of Endurance<br>Magic Gem<br>Thunder Ball<br>Perfection Gear<br>Exalted Gear'] // Zyrak info } else if (request == 'Zyrak, Vision of Madness'){ return [1200000000000,65,'Augment of Madness<br>Unstable Jewel<br>Veldarabloom<br>Scripture of Zyrak<br>Pulsating Stone<br>Bottled Chaos<br>Thunder Ball<br>Force of Veldara<br>Interstellar Vessel<br>Vault Tear<br>Vial of Insanity<br>Demonic Madness<br>Infinite Tower Spheroid<br>Transcended Extract<br>Tier 2 Booster Upgrade<br>Ghostly Gear<br>Boon of Vision<br>Ancestral Tombs'] // Simulation info } else if (request == 'Triworld Simulation'){ return [2400000000000,39,'20x Ask The Oracle Bundle<br>20x Remove Augment Bundle<br>5x Echo Trove<br>Augment of Simulation<br>Cosmic Mote<br>Amalgamation Blossoms<br>Catalysts of Accumulation<br>Faction Change<br>Codex Chapter 30<br>Codex Chapter 31<br>Triworld Experience Ward<br>Descendant Set Items<br>Quest Experience Potion<br>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]<img src="\/assets\/img\/getpoints\.webp">/i)) { const rgaName = document.body.innerHTML.match(/rg_sess_id=([A-Za-z0-9]+)">[\n\r]<img src="\/assets\/img\/getpoints\.webp">/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(/<strong>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 = `<li class="toolbarButtons"><a href="javascript:void(0);" id="notesDiv" onmouseover="statspopup(event,'<b>${name} Notes<b>')" onmouseout="kill()"><img src="https://studiomoxxi.com/moxximod/notes.png" height="25px" width="25px"></a></li>` // Open notes document.querySelector("#notesDiv").addEventListener('click', async function(){ createWindow(`${name} Notes`, "notes", 600, 250, 0); document.querySelector("#notes_content").innerHTML = `<textarea class="form-control-new" id="notesText"></textarea>` // 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 = '<img src="images/x.jpg" id="saveAndClose">' document.querySelector("#saveAndClose").setAttribute('onmouseover',`statspopup(event,'<b>Save and close</b>')`) 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 = `<img src=https://studiomoxxi.com/moxximod/bot.png height="20px" width="20px" id="mmOnOff">` // 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 = `<li class="toolbarButtons"><a href="javascript:void(0);" id="notesDiv" onmouseover="statspopup(event,'<b>MoxxiMod Tip:</b><br>${content}')" onmouseout="kill()"><img src="https://studiomoxxi.com/moxximod/tipIcon.webp" height="25px" width="25px"></a></li>` }; // 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(/<span style="color:#00FF00"> \(\+[0-9]+\)<\/span>/g,'').replace(/<span style="color:#[A-Za-z0-9]+">/g,'').replace(/,|%/g,''); itemData.slot = (html.match(/\[Slot - (.*?)\]<br\/>/i) || ['','slot unknown'])[1].toLowerCase(); itemData.img = (html.match(/<img src="([^"]*)" style="border:1px solid #666666;margin:2px;">/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 => `<img ${i.replace('(event','(event,')} onmouseout="kill()">`).join('') itemData.gems = 4-((html.match(/src="\/images\/gemslot2\.jpg"/g) || []).length); itemData.atk = parseInt((html.match(/\+([0-9]+) ATK<br>/i) || [0,0])[1]); itemData.hp = parseInt((html.match(/\+([0-9]+) HP<br>/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 size="3">(.*?)<\/font>/i) || ['er','er'])[1]; profileData.level = parseInt((html.match(/<font size="2">Level ([0-9]+)/i) || [0,0])[1]); profileData.class = (html.match(/<font size="2">Level [0-9]+ (.*?)<\/font>/i) || ['er','er'])[1]; profileData.exp = parseInt(((html.match(/<font size="1">TOTAL EXPERIENCE.*?<\/tr>/i) || '').toString().match(/>([0-9]+)</i) || [0,0])[1]); profileData.power = parseInt(((html.match(/<font size="1">TOTAL POWER.*?<\/tr>/i) || '').toString().match(/>([0-9]+)</i) || [0,0])[1]); profileData.attack = parseInt(((html.match(/<font size="1">ATTACK.*?<\/tr>/i) || '').toString().match(/>([0-9]+)</i) || [0,0])[1]); profileData.hp = parseInt(((html.match(/<font size="1">HIT POINTS.*?<\/tr>/i) || '').toString().match(/>([0-9]+)</i) || [0,0])[1]); profileData.growthyesterday = parseInt(((html.match(/<font size="1">GROWTH YESTERDAY.*?<\/tr>/i) || '').toString().match(/>([0-9]+)</i) || [0,0])[1]); profileData.elemental = parseInt(((html.match(/<font size="1">ELEMENTAL ATTACK.*?<\/tr>/i) || '').toString().match(/>([0-9]+)</i) || [0,0])[1]); profileData.resist = parseInt(((html.match(/<font size="1">ELEMENTAL RESIST.*?<\/tr>/i) || '').toString().match(/>([0-9]+)</i) || [0,0])[1]); profileData.chaos = parseInt(((html.match(/<font size="1">CHAOS DAMAGE.*?<\/tr>/i) || '').toString().match(/>([0-9]+)</i) || [0,0])[1]); profileData.wilderness = parseInt(((html.match(/<font size="1">WILDERNESS LEVEL.*?<\/tr>/i) || '').toString().match(/>([0-9]+)</i) || [0,0])[1]); profileData.godslayer = parseInt(((html.match(/<font size="1">GOD SLAYER LEVEL.*?<\/tr>/i) || '').toString().match(/>([0-9]+)</i) || [0,0])[1]); profileData.strength = parseInt((html.replace(/\s+/g,'').match(/role="progressbar"style="width:([0-9]+)%;height:8px;"/i) || [0,0])[1]); // Lookup faction section of profile and create string. Replace an empty loyalty level () with (0). const factionLookup = (html.match(/<b><[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(/<h5 class="card-title">EQUIPMENT<\/h5>/i,'').match(/<!--Equipment Items-->(.*?)<!--Skill Runes-->/i) || ['er','er'])[1].replace(/<div class="[^"]*">/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 style="position:absolute; left:100px; top:346px; width:99px; height:32px;text-align:center">.*?<\/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(/<!--God Kills-->.*?<!--Medals-->/i) || '').toString(); // !IMPORTANT! dont forget commas are removed from god names profileData.completedgodslayer = (godKills.match(/<b>.*?<\/b>/g) || ['<b>None</b>']).map(i => i.match(/<b>(.*?)<\/b>/i)[1]).join(','); const skillsCast = (html.match(/<!--Skills-->.*?<!--Start Left Column-->/i) || '').toString(); profileData.skills = { images: html.match(/<!--Skills-->.*?<!--Start Left Column-->/i) ? (html.match(/<!--Skills-->.*?<!--Start Left Column-->/i).toString().match(/<img align.*?ONMOUSEOUT="kill\(\)">/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 href="\/crew_profile\?id=[0-9]+">(.*?)<\/a>/i) || ['None','None'])[1]; profileData.crewid = parseInt((html.match(/<a href="\/crew_profile\?id=([0-9]+)">.*?<\/a>/i) || [0,0])[1]); profileData.underlings = (((html.match(/<table id="UnderlingTable" class="table table-striped-dark mt-1">.*?<\/table>/i) || '').toString().match(/profile\.php\?id=[0-9]+/g)) || []).length; profileData.underlingids = (((html.match(/<table id="UnderlingTable" class="table table-striped-dark mt-1">.*?<\/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(/<h6class="">(.*?)<\/h6>/i) || ['er','er'])[1]; homeData.level = parseInt((html.match(/<spanclass="toolbar_level">([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><td>([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><tdalign="right"><fontface="Verdana,Arial,Helvetica,sans-serif"size="1">([0-9]+)<\/font>/i)[1]); homeData.arcaneresist = parseInt(html.match(/ArcaneResist:<\/b><\/font><\/td><tdalign="right"><fontface="Verdana,Arial,Helvetica,sans-serif"size="1">([0-9]+)<\/font>/i)[1]); homeData.shadowresist = parseInt(html.match(/ShadowResist:<\/b><\/font><\/td><tdalign="right"><fontface="Verdana,Arial,Helvetica,sans-serif"size="1">([0-9]+)<\/font>/i)[1]); homeData.fireresist = parseInt(html.match(/FireResist:<\/b><\/font><\/td><tdalign="right"><fontface="Verdana,Arial,Helvetica,sans-serif"size="1">([0-9]+)<\/font>/i)[1]); homeData.kineticresist = parseInt(html.match(/KineticResist:<\/b><\/font><\/td><tdalign="right"><fontface="Verdana,Arial,Helvetica,sans-serif"size="1">([0-9]+)<\/font>/i)[1]); homeData.chaosresist = parseInt(html.match(/ChaosResist:<\/b><\/font><\/td><tdalign="right"><fontface="Verdana,Arial,Helvetica,sans-serif"size="1">([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><tdalign="right"><fontface="Verdana,Arial,Helvetica,sans-serif"size="1">([0-9]+)/i)[1]; homeData.mrupgrades = { purchased: parseInt((html.replace(/,/g,'').match(/MaximumRage:<\/b><\/font><\/td><tdstyle="padding-bottom:5px;"align="right"><fontface="VerdanaArialHelveticasans-serif"size="1">([0-9]+)\/[0-9]+/i) || [0,0])[1]), max: parseInt((html.replace(/,/g,'').match(/MaximumRage:<\/b><\/font><\/td><tdstyle="padding-bottom:5px;"align="right"><fontface="VerdanaArialHelveticasans-serif"size="1">[0-9]+\/([0-9]+)/i) || [0,0])[1]) }; return homeData; };