您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
View stats of active Goatling and swap them from anywhere.
// ==UserScript== // @name Goatlings Stat Viewer & Quick Swap Add-On // @namespace goatlings.statandswap // @description View stats of active Goatling and swap them from anywhere. // @version 2.0.0 // @license GPL // @grant GM.getValue // @grant GM.setValue // @grant GM.xmlHttpRequest // @match https://www.goatlings.com/* // ==/UserScript== // CHANGE ME (if you want): let showStats = true // default true let showList = true // default true let showScrollbar = false // default false, change to true if you to see the scrollbar on the Goatling list // (most) CSS can be changed at the bottom // GOATLING STAT VIEWER (updated for the tabs update!!!): GM.xmlHttpRequest( { method: "GET", url: "https://www.goatlings.com/MyGoatlings/", onload: parseResponse } ); // adding this for greasemonkey compatibility (plus gm_addstyle is deprecated) function addGlobalStyle(css) { var head, style; head = document.getElementsByTagName('head')[0]; if (!head) { return; } style = document.createElement('style'); style.type = 'text/css'; style.innerHTML = css; head.appendChild(style); } // credit arserbin3 on stackoverflow async function parseResponse(response) { const myGoatlingsHTML = response.responseText; const parser = new DOMParser(); const doc = parser.parseFromString(myGoatlingsHTML, "text/html"); // 1. Figure out what the active Goatlings name is: let activeGoatling = doc.querySelector("a[href='https://www.goatlings.com/mypets/']").innerHTML; let activeImage = doc.getElementById("active_pet_image").getElementsByTagName('img')[0].src; let activeHP = null, activeMood = null, activeLevel = null, activeHunger = null, activeEXP = null // 2. We have pain in the ass tabs now, so we gotta grab a list of em all along with their urls: let tabDivs = doc.querySelectorAll('.pv-cat'); let tabMap = {}; tabDivs.forEach(function(div) { let tabLink = div.querySelectorAll('a[href^="https://www.goatlings.com/MyGoatlings/manage/"]')[1]; if (tabLink) { let tabName = tabLink.textContent.trim(); let link = tabLink.getAttribute('href'); tabMap[tabName] = link; } }); // So that I can re-use as much of the existing code as possible, I'm just going to combine all of the tabs into one page async function fetchAndParse(url) { let response = await fetch(url); let text = await response.text(); return parser.parseFromString(text, "text/html"); } async function combineTheTabs(tabMap) { let allGoats = document.implementation.createHTMLDocument("All Goats"); for (let [tabName, url] of Object.entries(tabMap)) { let doc = await fetchAndParse(url); // Create a wrapper div so there can be some separation between the tabs even though they're all being combined onto one page let wrapperDiv = allGoats.createElement('div'); wrapperDiv.className = 'tab-content-wrapper'; wrapperDiv.setAttribute('data-tab-name', tabName); // Tab name is stored here in case it's needed in the future wrapperDiv.append(...doc.body.children); allGoats.body.appendChild(wrapperDiv); } return allGoats } let myGoatsDoc = await combineTheTabs(tabMap); let myGoats = Array.from(myGoatsDoc.querySelectorAll ("div.mystuff")); 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; if (currentGoat.includes("Goatlings:")) continue; // Filtering out the tabs amongst the goats 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(); } } // Doing some basic scaling: let scaleVal = 1; if (window.innerWidth < 1400){ scaleVal = 0.75 } if (window.innerWidth < 1300) { scaleVal = 0.6 } // STAT VIEWER CODE: 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","basedCard"); // 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 if (showStats === true){ document.body.appendChild(stats); } // QUICK SWITCHER CODE: // get the links to post to for switching goatlings let makeActiveMap = {}; myGoats.forEach(currentGoat => { if (currentGoat.innerText.includes("Goatlings:")) return; // Filtering out the tabs amongst the goats (again) let name = currentGoat.querySelector('p:nth-of-type(2)').textContent.trim(); let makeActiveLink = Array.from(currentGoat.querySelectorAll('a')).find(a => a.textContent.trim() === 'Make Active'); makeActiveMap[name] = makeActiveLink.href; }); // 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("based","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 if (showList === true){ 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 GM.xmlHttpRequest({ method: "GET", url: makeActiveMap[targetGoat], 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+")"; } // following is shamelessly lifted from w3schools if (showStats === true){ dragElement(document.getElementById("stats")); } if (showList === true){ 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)); } } } // EDIT HERE FOR STYLE! addGlobalStyle (` .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 { 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; } .basedCard { z-index:10000; padding:3px; } .basedList { z-index:10001; min-height:100px; max-height:284px; overflow-y:auto; overflow-x:hidden; min-width:135px; } `); if (showScrollbar === false){ addGlobalStyle (` .basedList { -ms-overflow-style: none; scrollbar-width: none; } .basedList::-webkit-scrollbar { display: none; } `); }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址