IMCraftingSum

Estimates inventory crafting xp

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         IMCraftingSum
// @namespace    http://tamper.net/
// @version      0.10
// @description  Estimates inventory crafting xp
// @author       Holychikenz
// @match        *://*.idlescape.com/*
// @run-at       document-end
// ==/UserScript==

function printCraftingXP() {
    // This is meant to give an idea of the exact crafting xp in inventory, this includes
    // the discrete nature of crafting (with the enchant) as resources spent smithing and cooking.
    // At the end intuition food and dono buff are assumed.
    // Verbose option for debugging, can be disabled, though it just prints to console.
    let verbose = false; // Set to true to see some extra print outs in the console
    function vp(text){
        if( verbose ) console.log(text);
    }
    // Internal Helper Functions for styling the message
    function sxp(xp, p=1, w=100){
        return `<b style="color:#66ccff;display:inline-block;width:${w}px;">${dnum(xp,p)} xp</b>`
    }
    function sgold(gold, p=1, w=100){
        return `<b style="color:#ffc107;display:inline-block;width:${w}px;">${dnum(gold,p)} gp</b>`
    }
    function lbar(key){
        return `<img src="/images/smithing/${key.toLowerCase()}_bar.png" style="display:inline;height:1em;">`
    }
    function lgem(key){
        return `<img src="/images/mining/${key.toLowerCase().replace(/ /g,"_")}.png" style="display:inline;height:1em;">`
    }
    let spn = `<span style="display: inline-block; width: 300px;">`
    // Default enchants assumed here, edit these if they do not apply
    let efficiency = 8; // gloves: Applies to smithing and Cooking
    let crafting = 6;
    let maplesInsteadOfPyreYews = false;

    let eMod = 1+0.01*efficiency;
    let cMod = 1-0.02*crafting;
    let buff = 1.3; // donation + intuition
    // Get the player inventory (or stash if that's open instead)
    var inventory = getInventory();
    var totalXP = 0;
    var totalHeat = 0; // Required to complete operations
    let msg = "<hr><h5>Crafting XP Report</h5><hr>";
    // Fert and branches, move above, also for ore need to save some logs
    // Lets assume sand is always the missing part, so lets just count sand
    // ******************************
    // Misc (Fert, Branch, Oil, Sage)
    // ******************************
    msg += `<div style="margin-left: 20px;">`;
    let fertCount = Math.floor(get(inventory, "Sand", 0) / 10 / cMod );
    let fertXP = fertCount * 20 * buff;
    msg += `${spn}${fertCount} fertilizer </span>${sxp(fertXP)}<br/>`;
    totalXP += fertXP;
    let branchCraft = Math.floor(get(inventory, "Branch", 0) / 20 / cMod )
    let branchXP = branchCraft * 10 * buff
    msg += `${spn}${branchCraft} branches </span>${sxp(branchXP)} <br/>`;
    vp(`Starting with ${inventory['Log']} logs`)
    // If no logs, set logs to zero
    inventory["Log"] = "Log" in inventory ? inventory["Log"] : 0;
    inventory["Oak Log"] = "Oak Log" in inventory ? inventory["Oak Log"] : 0;
    inventory["Willow Log"] = "Willow Log" in inventory ? inventory["Willow Log"] : 0;
    inventory["Maple Log"] = "Maple Log" in inventory ? inventory["Maple Log"] : 0;
    inventory["Yew Log"] = "Yew Log" in inventory ? inventory["Yew Log"] : 0;
    inventory['Log'] += branchCraft;
    vp(`Crafting ${branchCraft} branches brings us to ${inventory['Log']} logs`)
    let sageCount = Math.floor(get(inventory, "Sageberry Bush Seed", 0) / cMod)
    let sageXP = sageCount * 5000 * buff;
    msg += `${spn}${sageCount} sage </span>${sxp(sageXP)}<br/>`;
    totalXP += sageXP;
    // **********************
    // Smithing Tools
    // **********************
    let barxp = 0;
    let barcost = 0;
    // Save Styg and Rune, sell the rest, lets also account for used logs
    // Btw we assume pyro ring but no food (heat is infinite for IM race through pyres)
    let bardata = {
        "Bronze":     {"Ores":1,  "Heat": 1,   "XP": 130,    "gp":800,    "craftcount":40,  "logtype":"Log",        "logs":20},
        "Iron":       {"Ores":3,  "Heat": 3,   "XP": 480,    "gp":6000,   "craftcount":75,  "logtype":"Oak Log",    "logs":35},
        "Mithril":    {"Ores":5,  "Heat": 25,  "XP": 2893,   "gp":52000,  "craftcount":130, "logtype":"Willow Log", "logs":65},
        "Adamantite": {"Ores":10, "Heat": 50,  "XP": 8600,   "gp":480000, "craftcount":200, "logtype":"Maple Log",  "logs":100},
        "Runite":     {"Ores":15, "Heat": 100, "XP": 22013,  "gp":7200*0, "craftcount":270, "logtype":"Yew Log",    "logs":55},
        "Stygian":    {"Ores":25, "Heat": 250, "XP": 63000,  "gp":9600*0, "craftcount":350, "logtype":"Ichor",      "logs":175}
    }
    let daggerdata = {
        "Bronze":     {"Ores":1,  "Heat": 1,   "XP": 58,     "gp":400,    "craftcount":20,  "logtype":"Log",        "logs":5},
        "Iron":       {"Ores":3,  "Heat": 3,   "XP": 280,    "gp":4000,   "craftcount":50,  "logtype":"Oak Log",    "logs":10},
        "Mithril":    {"Ores":5,  "Heat": 25,  "XP": 2113,   "gp":52000,  "craftcount":100, "logtype":"Willow Log", "logs":25},
        "Adamantite": {"Ores":10, "Heat": 50,  "XP": 6580,   "gp":480000, "craftcount":160, "logtype":"Maple Log",  "logs":30},
        "Runite":     {"Ores":15, "Heat": 100, "XP": 18338,  "gp":7200*0, "craftcount":225, "logtype":"Yew Log",    "logs":45},
        "Stygian":    {"Ores":25, "Heat": 250, "XP": 50400,  "gp":9600*0, "craftcount":300, "logtype":"Ichor",      "logs":60}
    }
    msg += `</div><h6 style='margin-top:4px;margin-bottom:0px;'>Tools [Dagger, Hoe]</h6><div style="margin-left: 20px;">`;
    for( let key in bardata ){
        // First smelt the bars (subtracting needed heat), then craft and report remainders
        let value = bardata[key];
        let current_bars = get(inventory, `${key} Bar`, 0);
        let current_ore = get(inventory, `${key} Ore`, 0);
        current_ore = (key == "Bronze") ? Math.min( get(inventory, "Copper Ore", 0), get(inventory, "Tin Ore", 0) ) : current_ore;
        let new_bars = Math.floor(current_ore / value.Ores);
        let req_heat = new_bars * value.Heat;
        new_bars = Math.floor(new_bars * eMod)
        vp(`  Smith ${current_ore} ${key} into ${new_bars} bars using ${req_heat} heat`)
        // Split between daggers and hoes efficiently
        let dagger = daggerdata[key];
        let daggerCost = Math.ceil(dagger.craftcount*cMod);
        let hoeCost = Math.ceil(value.craftcount*cMod);
        let dagHoe = daggersAndHoes(new_bars+current_bars, daggerCost, hoeCost);
        let nDaggers = dagHoe[0];
        let nHoes = dagHoe[1];
        let barCost = nDaggers*daggerCost + nHoes*hoeCost;
        let woodCost = nDaggers*Math.ceil(dagger.logs*cMod) + nHoes*Math.ceil(value.logs*cMod);

        let bar_gold = nDaggers*dagger.gp + nHoes*value.gp
        // Do we have enough logs?
        //let logs_used = realLogCost * craftAttempts;
        if( woodCost > get(inventory, value.logtype, 0) ){
           msg += `<b style="color: red;">!! Need more ${value.logtype} for ${key} tools ${woodCost} > ${get(inventory, value.logtype, 0)} !!</b><br>`;
        }
        inventory[value.logtype] = inventory[value.logtype] - woodCost

        let leftOvers = (new_bars + current_bars) - barCost// realCraftCost*craftAttempts
        let dagHoeXP = nDaggers*dagger.XP*buff + nHoes*value.XP*buff
        //msg += `${spn}[${nDaggers},${nHoes}] ${key} tools (${leftOvers} excess)</span>${sxp(dagHoeXP)}${sgold(bar_gold)}<br/>`
        msg += `${spn}${lbar(key)} ${nDaggers} daggers, and ${nHoes} hoes (${leftOvers} excess)</span>${sxp(dagHoeXP)}${sgold(bar_gold)}<br/>`
        totalXP += dagHoeXP;
        barxp += dagHoeXP;
        barcost += bar_gold;
        totalHeat += req_heat;
    }
    vp(`Used ${totalHeat} heat for smithing (mith and up)`)
    // **********************
    // Jewelry + Gold Bars
    // **********************
    // Loop through gems to count max rings and compare with rings from gold
    msg += `</div><h6 style='margin-top:4px;margin-bottom:0px;'>Jewelry</h6><div style="margin-left: 20px;">`;
    let gemdata = {
        "Black Opal": {"XP": 6600, "gp":1500000},
        "Diamond": {"XP": 5600, "gp":300000},
        "Ruby": {"XP": 4600, "gp":200000},
        "Emerald": {"XP": 3600, "gp":140000},
        "Sapphire": {"XP": 2600, "gp":120000}
    }
    // How much gold do we have actually??
    let jew_gold = 0;
    let gold_bar_count = get(inventory, "Gold Bar", 0);
    let gold_ore = get(inventory, "Gold Ore", 0);
    let new_gold_bars = Math.floor( gold_ore / 10 );
    let gold_heat = new_gold_bars * 10;
    new_gold_bars = Math.floor( new_gold_bars * eMod );
    totalHeat += gold_heat;
    vp(`Up to ${totalHeat} heat used after gold`)
    //msg += `Smith ${new_gold_bars} Gold bars using ${gold_heat} heat<br>`;
    gold_bar_count += new_gold_bars;
    // let nrings = Math.floor( gold_bar_count / 150 / cMod ); // corrected by Kugan
    // With infinite gems, what is the maximum ring outcome?
    let finalRealRings = Math.floor( gold_bar_count / (50*cMod + 100*cMod**2) );
    let craftedRings = Math.ceil( finalRealRings * cMod )
    msg += `<i>Prepare ${craftedRings} gold rings to make ${finalRealRings} jewelry</i><br/>`
    // Gems time
    for( let key in gemdata ){
        let value = gemdata[key];
        let ngems = get(inventory, key, 0);
        if( craftedRings >= ngems ){
            let realGems = Math.floor( ngems / cMod );
            let gemxp = value.XP * realGems*buff;
            jew_gold += value.gp * realGems;
            totalXP += gemxp;
            craftedRings -= ngems;
            //msg += `${spn}${realGems} ${key} rings</span>${sxp(gemxp)}${sgold(value.gp*realGems)}<br>`
            msg += `${spn}${lgem(key)} ${realGems} rings</span>${sxp(gemxp)}${sgold(value.gp*realGems)}<br>`
        } else {
            let realGems = Math.floor( craftedRings / cMod );
            let gemxp = value.XP * realGems*buff;
            jew_gold += value.gp * realGems;
            totalXP += gemxp;
            msg += `${spn}${lgem(key)} ${realGems} rings</span>${sxp(gemxp)}${sgold(value.gp*realGems)}<br>`
            craftedRings = 0;
        }
    }
    msg += `</div><h6 style='margin-top:4px;margin-bottom:0px;'>Oils</h6><div style="margin-left: 20px;">`;
    // Lets make some pyres if we have fish oil
    let fishdata = {
        "Raw Shrimp": {"Heat": 5, "Oil": 0.05},
        "Raw Anchovy": {"Heat": 5, "Oil": 0.05},
        "Raw Trout": {"Heat": 10, "Oil": 0.1},
        "Raw Salmon": {"Heat": 10, "Oil": 0.1},
        "Raw Lobster": {"Heat": 15, "Oil": 0.15},
        "Raw Tuna": {"Heat": 15, "Oil": 0.15},
        "Raw Shark": {"Heat": 20, "Oil": 0.20}
    }
    let fish_oil = get(inventory, "Fish Oil", 0);
    for( let key in fishdata ){
        let value = fishdata[key];
        let current_fish = get(inventory, key, 0);
        let new_oil = Math.floor(value.Oil * current_fish * eMod);
        let req_heat = value.Heat * new_oil / value.Oil;
        totalHeat += req_heat;
        fish_oil += new_oil;
        //msg += `Cook ${key} for ${new_oil} oil, using ${req_heat} heat<br>`;
    }
    vp(`now at ${totalHeat} heat from cooking fish`)
    // Logs
    let logs = get(inventory, "Log", 0);
    let oaks = get(inventory, "Oak Log", 0);
    let willows = get(inventory, "Willow Log", 0);
    let maples = get(inventory, "Maple Log", 0);
    let yews = get(inventory, "Yew Log", 0);
    vp(`new log count is ${logs}`)
    // Do we have enough yews?
    if( maplesInsteadOfPyreYews ){
        if( maples/10 < fish_oil/4 ){
            msg += `<b style="color: red;">!! Need ${ fish_oil*4 - 10*maples } more Maple Logs for Pyres !!</b><br>`;
        } else {
            let pyreCount = Math.floor( fish_oil / 4 / cMod );
            maples -= Math.ceil( pyreCount * 10 * cMod );
            totalXP += pyreCount * 200 * buff
            let burnHeat = 800 * pyreCount
            totalHeat -= burnHeat
            //msg += `Craft ${pyreCount} pyre yew logs gaining ${burnHeat} heat<br>`
            msg += `${spn}${pyreCount} pyre maples</span>${sxp(pyreCount*200*buff)}</br>`
        }
    } else {
        if( yews < 2*fish_oil ){
            msg += `<b style="color: red;">!! Need ${ fish_oil*2 - yews } more Yew Logs for Pyres !!</b><br>`;
        } else {
            let pyreCount = Math.floor( fish_oil / 5 / cMod );
            yews -= Math.ceil( pyreCount * 10 * cMod );
            totalXP += pyreCount * 250 * buff
            let burnHeat = 3000 * pyreCount
            totalHeat -= burnHeat
            //msg += `Craft ${pyreCount} pyre yew logs gaining ${burnHeat} heat<br>`
            msg += `${spn}${pyreCount} pyre yews</span>${sxp(pyreCount*250*buff)}</br>`
        }
    }
    vp(`burning pyre brings the heat required to ${totalHeat}`)
    msg += `</div><h6 style='margin-top:4px;margin-bottom:0px;'>Ashes</h6><div style="margin-left: 20px;">`;
    // Finally ash some logs, takes 100 heat per try
    let inventory_heat = get(inventory, "Heat", 0) - totalHeat;
    //inventory_heat = 100e6 - totalHeat; // Infinite heat can help with pyre estimates
    vp(`Inventory - required heat: ${inventory_heat}`)
    // Check if we even have enough Yew logs to pay our heat debt
    if( inventory_heat + yews*200 <= 0 ){
        let k = Math.ceil( Math.abs(inventory_heat + yews*100)/100 )
        msg += `<b style="color: red;">!! Need ${ k } more Yew Logs to pay heat debt !!</b><br>`;
    }

    let ashxp = 0;
    let logAsh = Math.floor(logs / 30 / cMod);
    let oakAsh = Math.floor(oaks / 25 / cMod);
    let willowAsh = Math.floor(willows / 20 / cMod);
    let mapleAsh = Math.floor(maples / 15 / cMod);
    let currentAsh = logAsh + oakAsh + willowAsh + mapleAsh
    let ashHeat = Math.ceil( currentAsh * 100 * cMod )
    inventory_heat -= ashHeat
    vp(`Burn ${ashHeat} heat for ashing up to maple, bring us to ${inventory_heat}`)
    //msg += `Craft ${currentAsh} ash (up to Maple) using ${ashHeat} heat<br>`
    let burntYews = 0
    if( inventory_heat < 0 ){
        let burnYews = Math.ceil(Math.abs(inventory_heat/100))
        if( burnYews > yews ){
            msg += `<b style="color: red;">!! Need ${ burnYews - yews } more Yew Logs to pay heat debt !!</b><br>`;
        } else {
            //msg += `>> Need to quickly burn ${burnYews} yew logs for heat<br>`
        }
        yews -= burnYews;
        vp(`Need to burn ${burnYews} to keep up`)
        inventory_heat += burnYews*200;
    }
    // Yews are tricky, it takes 10.5 per to pay the heat, unless we ALREADY have enough heat, then just 10 per, so start with those
    let remainingAshHeat = Math.floor(inventory_heat / 100 / cMod)
    let firstYewAsh = Math.min(remainingAshHeat, Math.floor(yews/10/cMod))
    vp(`with ${remainingAshHeat} ashes, we can make ${firstYewAsh}`)
    // Remaining yews
    if( yews > 0 ){
        let yewAsh = Math.floor(yews/10 / cMod)
        //burntYews += Math.ceil(yewAsh / 2 * cMod)
        //msg += `Craft ${yewAsh} ash from Yews, burning ${burntYew} yew logs for heat<br>`
        currentAsh += yewAsh
    }

    ashxp = currentAsh * 50 * buff;
    totalXP += ashxp;
    msg += `${spn}${currentAsh} ashes </span>${sxp(ashxp)}<br>`
    msg += `</div>`
    msg += `<hr><div style="margin-left:20px;">${spn}<b>New Resources</b></span>${sxp( totalXP, 3)}${sgold(jew_gold + barcost, 3)}</div>`
    // Include current xp and gold
    let currentCraftXP = parseFloat( getSkills()["Crafting"] );
    let currentGold = getGold();
    msg += `<div style="margin-left:20px;">${spn}<b>Total</b></span>${sxp( totalXP+currentCraftXP, 3)}${sgold(currentGold + jew_gold + barcost, 3)}</div><hr>`
    let d = document.createElement("div");
    d.className = "chat-message"
    //d.innerHTML = `>> Crafting XP: ${totalXP.toFixed(0)}`;
    d.innerHTML = msg;
    document.getElementsByClassName("css-y1c0xs")[1].append(d);
}

// Maybe we could do something real quick here with cooking as well
function printCookingXP(IMRace=true){
    // Two ways to do this:
    // 1. Calculate all possible cooking (skip important veggies though)
    // 2. Optimize for the IM race.
    // Could optimize a lot more if we knew the burn chance formula
    // IMPORTANT: Due to the nature of the cooking time formula, the optimal cook weight is 14.38
    // so recipes after ~15 start to get penalized. Time per ingredient is stable between 10-20, below
    // 10 it goes up dramatically, so avoid that region.
    // RECIPES (sweet spot is 70 or 80 with salt)
    // 1. 4 ichor (salt required)
    // 2. 4 ash 1 ichor
    // 3. 4 tuna + (salmon | trout | anchovy | shrimp)
    // 4. 2 tuna 2 shark
    // 5. 1 banana 4 apple
}

function CookChance(xp, elvl, clvl=0){
    return Math.max(0, Math.min(1, (100 - 3*(xp/5)**(5/2)/elvl + 4*clvl)/100));
}

function CookTime(size){
    return 4**(0.95 + 0.05*size);
}

function daggersAndHoes(bars, dag, hoe) {
    // Assumption, Hoes are best, but only just, so we try for hoes
    // before daggers, but favor minimizing remaining bars over all else.
    let maxBig = Math.floor(bars/hoe);
    let hoeNots = 0
    let left = 1e6
    for( let s=0; s < maxBig+1; s++ ){
        let leftovers = bars - hoe*(maxBig - s);
        let final = leftovers%dag
        if( final < left ){
            left = final
            hoeNots = s
        }
    }
    let hoes = maxBig - hoeNots
    let daggers = Math.floor((bars - hoes*hoe)/dag)
    return [daggers, hoes]
}

// Helper functions to grab inventory
function getReactInstance(dom) {
    for( let key in dom ) {
        if( key.startsWith("__reactInternalInstance$") ) {
            return dom[key];
        }
    }
    return null;
}

function getReactHandler(dom) {
    for( let key in dom ){
        if( key.startsWith("__react") ){
            return dom[key];
        }
    }
    return null;
}

function get(object, key, default_value) {
    var result = object[key];
    return (typeof result !== "undefined") ? result : default_value;
}

function getSkills(){
    let skills = ["miningHeader", "foragingHeader", "fishingHeader", "farmingHeader", "enchantingHeader",
                  "runecraftingHeader", "smithingHeader", "craftingHeader", "cookingHeader", "constitutionHeader",
                  "attackHeader", "strengthHeader", "defenseHeader"];
    let skdict = {};

    try {
        for(let sk=0; sk<skills.length; sk++){
            let skill = document.getElementById(skills[sk]);
            let spans = skill.getElementsByTagName("span");
            let name = spans[0].getElementsByTagName("b")[0].innerHTML;
            let xp = spans[2].innerHTML.replace(/\D/g,'');
            skdict[name] = xp;
        }
    } catch(err) {}
    return skdict;
}

function getInventory() {
    let inventory_set = document.getElementsByClassName("inventory-container-all-items")[0];
    let dict = {};
    try{
        let inventory = inventory_set.getElementsByClassName("item");

        for(let i=0; i<inventory.length; i++) {
            let itemdiv = inventory[i];
            try {
                let k = getReactInstance(itemdiv);
                let item = k.return.pendingProps.item;
                let quant = k.return.pendingProps.quantity;
                dict[item.name] = quant;
            }
            catch(err){}
        }
        dict = sortObjectByKeys(dict);
        // Add heat and gold
        dict.Gold = getGold();
        dict.Heat = getHeat();
        let xp = getSkills();
        for(let k in xp) {
            dict[k] = xp[k];
        }
        dict.time = Date.now();
    }
    catch(err){}
    return dict;
}

function dnum(num, p) {
    let snum = ""
    if( num > 1000000 ){
        snum = `${(num/1e6).toFixed(p)} M`
    }
    else if( num > 1000 ){
        snum = `${(num/1e3).toFixed(p)} k`
    }
    else{
        snum = `${num}`
    }
    return snum
}

function numberWithCommas(x) {
  return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}


function getGold(){
    try {
        return parseInt(document.getElementById("gold-tooltip").getElementsByTagName("span")[0].innerHTML.replace(/,/g, "").replace(/\./g, ""));
    }
    catch(err){
        return 0;
    }
}

function getHeat(){
    try{
        return parseInt(document.getElementById("heat-tooltip").getElementsByTagName("span")[0].innerHTML.replace(/,/g, "").replace(/\./g, ""));
    }
    catch(err){
        return 0;
    }
}

function sortObjectByKeys(o){
    return Object.keys(o).sort().reduce((r,k) => (r[k] = o[k], r), {});
}


// Modal Settings
(function() {
    var modal_css = document.createElement("style");

    modal_css.innerHTML =
`
.ps_settings {
  display: none;
  position: fixed;
  z-index: 9999;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  overflow: auto;
  background-color: rgb(0, 0, 0);
  background-color: rgba(0, 0, 0, 0.4);
}
.ps_settings_content {
  background-color: rgba(25, 66, 100, 0.90);
  border-radius: 5px;
  margin: 15% auto;
  padding: 20px;
  border: 1px solid #888;
  width: 80%;
}
.close_ps_settings {
  color: #aaa;
  float: right;
  font-size: 28px;
  font-weight: bold;
}
.close_ps_settings:hover, close_ps_settings:focus {
  color: black;
  text-decoration: none;
  cursor: pointer;
}
.ps_button {
	background-color:transparent;
	border-radius:4px;
	border:2px solid #7f7f7f;
	display:inline-block;
	cursor:pointer;
	color:#ffffff;
	padding:7px 25px;
	text-decoration:none;
	text-shadow:0px 1px 0px #e1e2ed;
}
.ps_button:hover {
	background-color:rgba(255,255,255,0);
}
.ps_button:active {
	position:relative;
	top:1px;
}
`

    document.body.appendChild(modal_css);

    // Crafting button
    var craft_button = document.createElement("BUTTON");
    craft_button.id = "craftingbtn";
    craft_button.setAttribute("style", "position: absolute; top: 5px; left: 20%;");
    craft_button.zIndex = "7000";
    craft_button.className = "ps_button";
    craft_button.innerHTML = "CraftXP";
    craft_button.onclick = () => printCraftingXP();
    document.body.appendChild(craft_button);
})();