Goatlings Stat Viewer & Quick Swap Add-On

View stats of active Goatling and swap them from anywhere.

Mint 2023.01.29.. Lásd a legutóbbi verzió

// ==UserScript==
// @name     Goatlings Stat Viewer & Quick Swap Add-On
// @namespace goatlings.statandswap
// @description View stats of active Goatling and swap them from anywhere.
// @version  1.1.8
// @license GPL
// @grant        GM_addStyle
// @grant           GM.getValue
// @grant           GM.setValue
// @grant           GM.xmlHttpRequest
// @match https://www.goatlings.com/*
// ==/UserScript==

//  GOATLING STAT VIEWER:

GM.xmlHttpRequest( {
    method:     "GET",
    url:        "https://www.goatlings.com/mypets/",
    onload:     parseResponse
} );

async function parseResponse(response) {
    let activeGoatling = document.querySelector("a[href='https://www.goatlings.com/mypets/']").innerHTML;
    let activeImage = document.getElementById("active_pet_image").getElementsByTagName('img')[0].src;
    let activeHP = null, activeMood = null, activeLevel = null, activeHunger = null, activeEXP = null
    let parser = new DOMParser ();
    // query /mypets to grab some juicy stats
    let myGoatsDoc = parser.parseFromString(response.responseText, "text/html");
    let myGoats = Array.from(myGoatsDoc.querySelectorAll ("div.mypets-pet"));
    let myGoatNames = [], goatData = null, splitData = null
    //iterate through goatling stats and find which belongs to active
    for (let goat of myGoats){
        let currentGoat = goat.innerText;
        myGoatNames.push(currentGoat.split("\n")[2].trim());
        if (currentGoat.includes(activeGoatling)){
            goatData = currentGoat;
        }
    }
    splitData = goatData.split("\n");
    for (let data of splitData){
        if (data.includes("Level:")){
            activeLevel = data.trim();
        } else if (data.includes("HP:")){
            activeHP = data.trim();
        } else if (data.includes("Hunger:")){
            activeHunger = data.trim();
        } else if (data.includes("Mood:")){
            activeMood = data.trim();
        } else if (data.includes("EXP:")){
            activeEXP = data.trim();
        }
    }

    // check if we'll need to do some scaling
    let scaleVal = 1;
    if (window.innerWidth < 1400){
        scaleVal = 0.75
    } if (window.innerWidth < 1300) {
        scaleVal = 0.6
    }

    //start cooking up the div to display the stats
    let stats = document.createElement("div");
    stats.id = 'stats'
    if (activeGoatling.length > 18){
        stats.innerHTML = "<div style='text-align:center;font-size:16px'>" + activeGoatling + "</div>";
    } else{
        stats.innerHTML = "<div style='text-align:center'>" + activeGoatling + "</div>";
    }
    stats.innerHTML += "<div style='border-radius:12px;background:white;width:80%;display:flex;justify-content:center;margin:auto'><div style='position:relative;left:2.5px'><img src=" + activeImage +"></div></div>";
    // making the font size for exp smaller when it's huge
    if (activeEXP.length >= 20){
        stats.innerHTML += activeLevel + "<br /><div style=font-size:14px>" + activeEXP + "</div>" + activeHP + "<br />" + activeHunger + "<br />" + activeMood;
    } else{
        stats.innerHTML += activeLevel + "<br />" + activeEXP + "<br />" + activeHP + "<br />" + activeHunger + "<br />" + activeMood;
    }
    stats.classList.add("based");
    // grabbing the stored values for the element coordinates (default to 10)
    stats.style.left = await GM.getValue("startX", 10);
    stats.style.top = await GM.getValue("startY", 10);
    stats.style.transform = "scale(" + scaleVal + "," + scaleVal + ")";
    // add it into the page
    document.body.appendChild(stats);

    // now for the quick switcher
    // get the links to post to for switching goatlings
    let makeActiveForms = Array.from(myGoatsDoc.getElementsByTagName('form')).slice(1);
    let makeActiveLinks = makeActiveForms.map((x) => { return x.action; });
    // find the csrf_test_name to add to the post request
    let makeActiveInputs = Array.from(myGoatsDoc.getElementsByTagName('input'));
    let csrfName = null
    for (let item of makeActiveInputs){
        if (item.name === "csrf_test_name"){
            csrfName = item.value;
            break;
        }
    }
    let postString = "csrf_test_name=" + csrfName + "&s=Make+Active+Goatling"

    // start cooking up the div to display your goats
    let goatList = document.createElement("div");
    goatList.id = 'goatList'
    goatList.innerHTML = "<div style='text-align:center'> My Goatlings </div>";
    // add each goat name as a button
    for (let goat of myGoatNames){
        goatList.innerHTML += "<div class='basedButtonGroup' id='swapButton'><button class='basedButton'>" + goat + "</button></div>"
    }
    goatList.classList.add("basedList");
    goatList.style.left = await GM.getValue("listX", 10);
    goatList.style.top = await GM.getValue("listY", 10);
    goatList.style.transform = "scale("+scaleVal+","+scaleVal+")";
    // add it into the page
    document.body.appendChild(goatList);

    // function to send a post request to tell the game we want to swap to a specific goatling
    function swapGoatling(event){
        let targetGoat = event.currentTarget.innerText, targetIndex = myGoatNames.indexOf(targetGoat);
        console.log("Swapping in " + targetGoat + "... their ID is: " + targetIndex);
        GM.xmlHttpRequest({
            method: "POST",
            url: makeActiveLinks[targetIndex],
            data: postString,
            headers: {
                "Content-Type": "application/x-www-form-urlencoded"
            },
            onload: function(response) {
                // refresh after requesting
                window.location = document.URL;
            }

        });
    }

    let goatButtons = document.querySelectorAll("#swapButton");
    // add in a listener, so once the buttons are clicked, the function is called
    for (let goatButton of goatButtons){
        goatButton.addEventListener ("click", swapGoatling, false);
    }

    // adjust scale when resizing past some thresholds
    window.addEventListener("resize", reSize);
    function reSize(){
        let scaleVal = 1;
        if (window.innerWidth < 1400){
            scaleVal = 0.75
        } if (window.innerWidth < 1300) {
            scaleVal = 0.6
        }
        stats.style.transform = "scale("+scaleVal+","+scaleVal+")";
        goatList.style.transform = "scale("+scaleVal+","+scaleVal+")";
    }

    // ollowing is shamelessly lifted from w3schools
    dragElement(document.getElementById("stats"));
    dragElement(document.getElementById("goatList"));

    function dragElement(elmnt) {
        let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
        elmnt.onmousedown = dragMouseDown;

        function dragMouseDown(e) {
            e = e || window.event;
            e.preventDefault();
            pos3 = e.clientX;
            pos4 = e.clientY;
            document.onmouseup = closeDragElement;
            document.onmousemove = elementDrag;
        }

        function elementDrag(e) {
            e = e || window.event;
            e.preventDefault();
            // calculating new cursor position:
            pos1 = pos3 - e.clientX;
            pos2 = pos4 - e.clientY;
            pos3 = e.clientX;
            pos4 = e.clientY;
            elmnt.style.top = (elmnt.offsetTop - pos2) + "px";
            elmnt.style.left = (elmnt.offsetLeft - pos1) + "px";
        }

        async function closeDragElement(e) {
            document.onmouseup = null;
            document.onmousemove = null;

            e = e || window.event;
            e.preventDefault();
            pos1 = pos3 - e.clientX;
            pos2 = pos4 - e.clientY;
            pos3 = e.clientX;
            pos4 = e.clientY;

            // saving element co-ordinates for some level of location persistence
            let setValY = "startY", setValX = "startX";
            // need to keep 2 separate values for both elements
            if (elmnt.id === "goatList"){
                setValY = "listY";
                setValX = "listX";
            }
            GM.setValue(setValY, (elmnt.offsetTop - pos2));
            GM.setValue(setValX, (elmnt.offsetLeft - pos1));
        }
    }
}

GM_addStyle (`
    .basedButtonGroup .basedButton {
        background-color: #956e43;
        border: 4px solid #ad8a60;
        color: white;
        padding: 5px 10px;
        text-align: center;
        text-decoration: none;
        font-size: 15px;
        cursor: pointer;
        min-width: 122px;
        display: block;
        margin: auto;
        border-radius:8px;
        text-shadow:-2px -2px 0 #ae8c5c, 2px -2px 0 #ae8c5c, -2px 2px 0 #ae8c5c, 2px 2px 0 #ae8c5c;
        font-weight:bold;
        font-family:Trebuchet MS;
        margin-bottom: 2px;

    }

    .basedButtonGroup .basedButton:not(:last-child) {
        border-bottom: none;
    }

    .basedButtonGroup .basedButton:hover {
        background-color: #755839;
    }

    .based {
        z-index:10000;
        font-family:Trebuchet MS;
        text-shadow:-1px -1px 0 #a2bfa1, 1px -1px 0 #a2bfa1, -1px 1px 0 #a2bfa1, 1px 1px 0 #a2bfa1;
        cursor:move;
        position:absolute;
        border-radius:12px;
        font-size:21px;
        color:white;
        font-weight:bold;
        background-image:url('https://www.goatlings.com/images/layout/greenbg.gif');
        border:3px rgb(220,228,220) solid;
        padding:3px";
        }

    .basedList {
        z-index:10001;
        font-family:Trebuchet MS;
        text-shadow:-1px -1px 0 #a2bfa1, 1px -1px 0 #a2bfa1, -1px 1px 0 #a2bfa1, 1px 1px 0 #a2bfa1;
        cursor:move;
        position:absolute;
        border-radius:12px;
        font-size:21px;
        color:white;
        font-weight:bold;
        background-image:url('https://www.goatlings.com/images/layout/greenbg.gif');
        border:3px rgb(220,228,220) solid;
        padding:3px";
        min-height:100px;
        max-height:300px;
        overflow-y:auto;
        overflow-x:hidden;
        min-width:135px;
    }
`);