您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds keyboard controls around GC
当前为
// ==UserScript== // @name GC Keyboard Controls Bundle ALPHA // @namespace grundos-cafe // @icon https://www.google.com/s2/favicons?sz=64&domain=grundos.cafe // @version 2.0 // @description Adds keyboard controls around GC // @author mox with code by Z & Dij & Berna & Kait & Shiba & Sanjix // @match https://www.grundos.cafe/* // @match https://www.grundos.cafe/faerieland/employ* // @match https://www.grundos.cafe/faerieland/darkfaerie* // @match https://www.grundos.cafe/halloween/esophagor* // @match https://www.grundos.cafe/island/kitchen* // @match https://www.grundos.cafe/winter/snowfaerie* // @match https://www.grundos.cafe/halloween/witchtower* // @match https://www.grundos.cafe/halloween/braintree* // @match https://www.grundos.cafe/water/fishing* // @match https://www.grundos.cafe/plateau/omelette* // @match https://www.grundos.cafe/*jelly* // @match https://www.grundos.cafe/island/tombola* // @match https://www.grundos.cafe/desert/fruitmachine* // @match https://www.grundos.cafe/faerieland/tdmbgpop* // @match https://www.grundos.cafe/wishing* // @match https://www.grundos.cafe/*merrygoround* // @match https://www.grundos.cafe/*tikitours* // @match https://www.grundos.cafe/*wheel* // @match https://www.grundos.cafe/*foodclub* // @match https://www.grundos.cafe/medieval/*king* // @match https://www.grundos.cafe/dome/battlepedia* // @match https://www.grundos.cafe/bank* // @match https://www.grundos.cafe/safetydeposit* // @match https://www.grundos.cafe/market/wizard* // @match https://www.grundos.cafe/market/browseshop/?owner=* // @match https://www.grundos.cafe/games/*dicearoo* // @match https://www.grundos.cafe/games/gormball* // @match https://www.grundos.cafe/games/bilgedice* // @match https://www.grundos.cafe/games/*slots* // @match https://www.grundos.cafe/games/snowwars* // @match https://www.grundos.cafe/games/pyramids* // @match https://www.grundos.cafe/medieval/kissthemortog* // @match https://www.grundos.cafe/games/tyranuevavu* // @match https://www.grundos.cafe/games/cheat/play* // @match https://www.grundos.cafe/dome/1p/*battle* // @match https://www.grundos.cafe/adopt* // @match https://www.grundos.cafe/games/lottery* // @license MIT // @require https://code.jquery.com/jquery-3.7.1.js // @grant GM.getValue // @grant GM.setValue // @grant window.close // ==/UserScript== /////////////////// BEGIN USER SETTINGS //////SIDEBAR NAVIGATION // by default, this script links the Q/D/T/W keys // to sidebar links from any page on the site: // Q navigates to your first available sidebar Quest item // D navigates to your first available sidebar Dailies item // T navigates to your first available sidebar Timelies item // W navigates to your first available pet to fish (W for water) // if you don't want one or more of these keys to do this, toggle them below const qKeyNavigation = true; const dKeyNavigation = true; const tKeyNavigation = true; const wKeyNavigation = true; // by default, the Dailies/Timelies "_ with all" and "Quick _" key for is "B" // but you may change it to a different letter or numpad key if preferred const doItAllQuick = "KeyB"; //options: "Key[letter]" "Numpad[number]" // NB: this applies to Fishing, Lottery, Wishing Well, Tiki Tour, Merry-Go-Round // by default, the number keys (above QWERTY) map to the locations in the order of your sidebar and update dynamically // (e.g. esophagor button goes away when time to go to brain tree, no faerie quest button unless you have one) // if you want to override this function and map the number keys (above QWERTY) to the same quest locations every time, // toggle the override to 'true' and arrange the location URLs to your liking, leaving the numbers in the same place const questSidebarOverride = false; //false (default): number keys map 1:1 with visual buttons (and change as quests are completed) //const questSidebarOverride = true; //true: number keys map exactly as below, every time const questOverrideActions = [ // digit key: link to quest {1: "/halloween/witchtower/"}, //Edna Quest {2: "/halloween/esophagor/"}, //Esophagor Quest {3: "/winter/snowfaerie/"}, //Taelia Quest {4: "/island/kitchen/"}, //Kitchen Quest {5: "/halloween/braintree/"}, //Brain Tree Quest {6: "/faerieland/darkfaerie/"}, //Jhudora's Quest {7: "/faerieland/employ/"}, //Faerieland Employment Agency {8: "/faerieland/quests/"} //Faerie Quests ]; // NB: you can only navigate between the quest pages in this way when on one of the pages listed above //////WISHING WELL FILL-IN // by default, this script will input the recommended donation of 25 NP // and a default wish of "relic," but you may change the item or remove // the fill-in altogether if you want to type in both boxes every time const wwFillInHelp = true; const wwItemChoice = "relic";//change to "" to enter a new item each time but keep the NP fill-in const wwDonationAmount = 25; //////LOTTERY TICKETS // by default, this script generates minimally-overlapping lottery numbers // based on this model: https://www.andrew.cmu.edu/user/kmliu/neopets/lottery2.html // but if you don't want it to do that, you can override it here const lotteryTicketModel = true; // NB: Quick Pick can be used with the override on/off, disabling this override would // allow you to pick all the lottery ticket numbers yourself for every ticket //////GORMBALL // choose your Gormball player and preferred wait time const gormPlayer = 5; //1=Thyassa, 2=Brian, 3=Gargarox, 4=Farvin III, 5=Ember, 6=COOL Zargrold, 7=Ursula, 8=Kevin const gormWait = 1; // 1-5 (in seconds), 1 is default //////KISS THE MORTOG // choose your Kiss The Mortog alert: 100, 300, 1150, 5900 [avatar], 35000, 250000, 2000000, 18000000 const mortogLimit = 5900; // put "first" or "last" to choose only the first or last mortogs that appear in line // by default, a random (by computing standards) mortog will be chosen each time const mortogFavorite = "random"; // "random"; "first"; "last"; //////BILGE DICE // choose your Bilge Dice ante; if you haven't yet unlocked the option you select, you will bet 10 NP const bilgeAnteAmount = 10; // choose from below values, all are in NP const bilgeAnteChoices = [ 10, 50, 100, 200, 500, 1000 ]; /////////////////// END USER SETTINGS ////////////////////////////////////////////////////////////////////////// // please see the bottom of the script for references! /* global $ */ //initialize enhancements in functions and aorund the site if (location.pathname.match(/bilgedice/)) bilgeDice(); if (location.pathname.match(/kissthemortog/)) kissTheMortog(); if (location.pathname.match(/slots/)) scorchySlots(); if (location.pathname.match(/snowwars/)) snowWars(); if (location.pathname.match(/pyramids/)) pyramids(); if (location.pathname.match(/tyranuevavu/)) tyranuEvavu(); if (location.pathname.match(/cheat/)) {cheat();} if (location.pathname.match(/medieval\/.*king/)) $(function(){gwKing()}); if (location.pathname.match(/foodclub\/current_bets/)) fcBetCount(); if (location.pathname.match(/lottery/) && lotteryTicketModel) lotteryTicketNumbers(); if (location.pathname.match(/wishing/) && wwFillInHelp) { document.querySelector("input[name=donation]").value = wwDonationAmount; document.querySelector("input[name=wish]").value = wwItemChoice;} window.addEventListener("keydown", (event) => { // credit to Z and Dij if(event.target.matches("input[type='text'], input[type='search'], input[type='number'], input[type='password'], textarea, [contenteditable=true]")) {return;} if(event.isComposing) {return;} //if entering text in a text box, don't record the event if(event.repeat) {return;} //don't respond to someone holding down a key such that it repeats if(event.altKey) {return;} //if pressing alt, if(event.ctrlKey) {return;} // ctrl, if(event.shiftKey) {return;} // shift, if(event.metaKey) {return;} //or mac:cmd pc:win with a key, don't record the event let bdStrMedW = 0; let arrowKeyCount = 0; let digitKeyCount = 0; //initialize some useful variables switch (event.code) { //Space - select all in Bilge Dice and Scorchy Slots, plus general "click confirm" case "Space": event.preventDefault(); if (location.pathname.match(/bilgedice\/play/)) { bilgeDice('Space'); break; //prevent fall-through when playing Bilge Dice since Space is used differently } else if (location.pathname.match(/slots/)) { scorchySlots('Space'); break; //prevent fall-through when playing Scorchy Slots since Space is used differently } //Enter & NumpadEnter //Space falls through along with NumpadEnter case "NumpadEnter": // so all three keys can be used interchangeably case "Enter": // as general "click to confirm" control keys if (location.pathname.match(/safetydeposit/)) { //remove one from SDB const SDBrmOne = document.querySelector("a.sdb-remove-one-text"); if (SDBrmOne) SDBrmOne.click(); } else if (location.pathname.match(/market\/browseshop/)) { //buy the searched item in a usershop const USbuy = document.querySelector("#searchedItem.shop-item input[type='image']"); if (USbuy) USbuy.click(); } else if (location.pathname.match(/market\/wizard/)) { //search the SW and navigate to the first shop shown const SWsearch = document.querySelector("div.sw_search_submit input[value='Search']"); const SWshop = document.querySelector(".market_grid.sw_results .data a:nth-child(1)"); if (SWshop) SWshop.click(); else if (SWsearch) SWsearch.click(); } else if (location.pathname.match(/witchtower|braintree|esophagor|kitchen|snowfaerie|employ|darkfaerie/)) {// credit to Z and Dij const questStart = document.querySelector("#page_content form[action*='accept'] .form-control"); const questComplete = document.querySelector("#page_content .form-control[onclick*='complete']"); const questRestart = document.querySelector("#page_content .form-control:not([value*='Return'])"); const formButtons = document.querySelectorAll("#page_content form .form-control:not([type=\"text\"]), .form-control[type=button]"); if (questStart) questStart.click(); else if (questComplete) questComplete.click(); else if (questRestart) questRestart.click(); else if (formButtons.length === 1) {window.location.reload();} } else if (location.pathname.match(/dicearoo/)) { const dicearooRA = document.querySelector("form[id='roll-again'] > input[type='submit']"); const dicearooPM = document.querySelector("input[value='Press Me']"); const dicearooPlay = document.querySelector("form[action*='play_dicearoo'] > input[type='submit']"); if (dicearooPlay) dicearooPlay.click(); else if (dicearooRA) dicearooRA.click(); else if (dicearooPM) dicearooPM.click(); } else if (location.pathname.match(/dome\/1p/)) { const BDgo = document.querySelector("input[value='Go!']:not(.ignore-button-size)"); const BDnext = document.querySelector("input[value='Next']"); const BDrematch = document.querySelector("input[value='Rematch!']"); if (BDgo) BDgo.click(); else if (BDnext) BDnext.click(); else if (BDrematch) BDrematch.click(); } else if (location.pathname.match(/adopt/)) { const adoptNext = document.querySelector("input[value='Find a Neopet at Random']"); if (adoptNext) adoptNext.click(); } else if (location.pathname.match(/gormball/)) { let chooseAGorm = $(`.gormball_player[data-id="${gormPlayer}"]`); const playAGorm = $("input[value='Next >>>'], input[value='Throw!'], input[value='Continue!']"); if (chooseAGorm) chooseAGorm.click(); if (playAGorm) {$("select[name='turns_waited']").prop('selectedIndex', gormWait-1); playAGorm.click();} } else if (location.pathname.match(/lottery/)) { const lotteryBuy = document.querySelector(".form-control[value='Buy a Lottery Ticket!']"); if (lotteryBuy) lotteryBuy.click(); } else if (location.pathname.match(/halloween\/wheel|prehistoric\/wheel|faerieland\/wheel|brightvale\/wheel/)) { const spinWheel = document.querySelector('input[value*="Spin the Wheel"]'); if(spinWheel) spinWheel.click(); } else if (location.pathname.match(/water\/fishing/)) { const fishOnce = document.querySelector("input[value='Reel in Your Line']"); const castAgain = document.querySelector("a:has(img[title*='is ready to fish!'])"); const setActivePet = document.querySelector("img[title^='Set Active Pet to '"); if (fishOnce) fishOnce.click(); else if (castAgain) castAgain.click(); else if (!castAgain && setActivePet) setActivePet.click(); } else if (location.pathname.match(/foodclub\/bet/)) { const fcPlaceBet = document.querySelector('input[value="Place this bet!"]'); if (fcPlaceBet) fcPlaceBet.click(); } else if (location.pathname.match(/medieval\/.*king/)) { const btnKing = document.querySelector("input[value*='the King']"); if (btnKing) btnKing.click(); } else if (location.pathname.match(/bank/)) { const bankInterest = document.querySelector('input[value*="Collect Interest"]'); if (bankInterest) bankInterest.click(); } else if (location.pathname.match(/medieval\/symolhole/)) { const symolHole = document.querySelector('input[value="Enter!"]'); if (symolHole) symolHole.click(); } else if (location.pathname.match(/desert\/shrine/)) { const coltzansShrine = document.querySelector('input[value="Approach the Shrine"]'); if (coltzansShrine) coltzansShrine.click(); } else if (location.pathname.match(/faerieland\/springs/)) { const healmypets = document.querySelector('input[value="Heal My Pets"]'); if (healmypets) healmypets.click(); } else if (location.pathname.match(/jelly\/greenjelly/)) { const greenJelly = document.querySelector('input[type="submit"].form-control.half-width'); if (greenJelly) greenJelly.click(); } else if (location.pathname.match(/jelly\/jelly/)) { const jelly = document.querySelector('input[value="Grab some Jelly"]'); if (jelly) jelly.click(); } else if (location.pathname.match(/plateau\/omelette/)) { const omelette = document.querySelector('input[value="Grab some Omelette"]'); if (omelette) omelette.click(); } else if (location.pathname.match(/island\/tombola/)) { const tombola = document.querySelector('input[value="Play Tombola!"]'); if (tombola) tombola.click(); } else if (location.pathname.match(/desert\/fruitmachine/)) { const fruitMachine = document.querySelector('input[value="Spin The Wheel!!!"]'); if (fruitMachine) fruitMachine.click(); } else if (location.pathname.match(/faerieland\/tdmbgpop/)) { const tdmbgpop = document.querySelector('input[value="Talk to the Plushie"]'); if (tdmbgpop) tdmbgpop.click(); } else if (location.pathname.match(/island\/training|pirates\/academy/)) { const courseSubmit = document.querySelector('input[value="Start Course"]'); const payCourse = document.querySelector('input[value="Pay"]'); const finishTraining = document.querySelector('input[value="Complete Course!"]'); if (courseSubmit) courseSubmit.click(); if (payCourse) payCourse.click(); if (finishTraining) finishTraining.click(); } else if (location.pathname.match(/pirates\/buriedtreasure/)) { const enterMap = document.querySelector('input[value="Click to Play"]'); const mapClick = document.querySelector('input[class="clickable-image"]'); if (enterMap) enterMap.click(); if (mapClick) mapClick.click(); } else if (location.pathname.match(/kiosk/)) { const kioskBuy = document.querySelector('button[onclick*="purchase-scratchcard"]'); const kioskScratch = document.querySelector('input[value="Scratch!"]'); if (kioskBuy) kioskBuy.click(); else if (kioskScratch) kioskScratch.click(); } else if (location.pathname.match(/scratchcard/)) { var scSpots; // credit to Sanjix if (location.pathname.match(/halloween|winter/)) scSpots = document.querySelectorAll('#scratchcard a[href*="tile"]:has(img[src$="0.gif"])'); if (location.pathname.match(/desert/)) scSpots = document.querySelectorAll('#scratchcard a[href*="tile"]:not(:has(img))'); let scSpotsRand = Math.floor(Math.random() * Math.floor(scSpots.length)); if (scSpotsRand < scSpots.length) scSpots[scSpotsRand].click(); } else if (location.pathname.match(/tyranuevavu/)) { const newGame = document.querySelector('input[value="Play Now!"]'); const playAgain = document.querySelector('input[value="Play Again"]'); if (newGame) newGame.click(); if (playAgain) playAgain.click(); } else if (location.pathname.match(/cheat/)) { const playAgain = document.querySelector('main input[value="Play Again"]'); const resumePlay = document.querySelector('main input[value="Return to Game"]'); const nextDiff = document.querySelector('input[value^="Continue to Difficulty"]'); const playResolve = document.querySelector('input[value="Click to Continue"]'); const myTurn = document.querySelector('main input[value="Go!"]'); if (playAgain) {playAgain.click();} else if (resumePlay) {resumePlay.click();} else if (nextDiff) {nextDiff.click();} else if (playResolve) {playResolve.click();} else if (myTurn) {myTurn.click();} } else if (location.pathname.match(/bilgedice/)) { bilgeDice('Enter'); } else if (location.pathname.match(/kissthemortog/)) { kissTheMortog('Enter'); } else if (location.pathname.match(/slots/)) { scorchySlots('Enter'); } else if (location.pathname.match(/snowwars/)) {snowWars('Enter'); } else if (location.pathname.match(/pyramids/)) {pyramids('Enter'); } break; // Arrow Keys case "ArrowDown": arrowKeyCount++; //falls through, select the fourth item with down arrow case "ArrowRight": arrowKeyCount++; //falls through, select the third item with right arrow case "ArrowUp": arrowKeyCount++; //falls through, select the second item with up arrow case "ArrowLeft": if (location.pathname.match(/witchtower|braintree|esophagor|kitchen|snowfaerie|employ|darkfaerie|dome\/1p|tyranuevavu|cheat|kiosk/)) event.preventDefault(); //don't move the page arrowKeyCount++; //select the first item with left arrow if (location.pathname.match(/witchtower|braintree|esophagor|kitchen|snowfaerie|employ|darkfaerie/)) {// credit to Z and Dij let questItemList = document.querySelectorAll(".shop-item"); if (!questItemList.length) questItemList = document.querySelectorAll(".quest-item"); if (arrowKeyCount < questItemList.length) { let itemInInv = questItemList[arrowKeyCount].querySelector(`img.search-helper-in-inv`); let itemInSDB = questItemList[arrowKeyCount].querySelector(`img.search-helper-sdb-exists`); if (itemInInv) {console.log("since the item is already in your inv, you don't need to search anywhere for it!");break;} else if (itemInSDB) { //if the item already exists in your SDB, click that icon to get it itemInSDB.click(); } else { //if neither, search it on the SW questItemList[arrowKeyCount].querySelector(`img.search-helper-sw`).click(); } } else {break;} } else if (location.pathname.match(/dome\/1p/) && document.querySelector('#bd-form select#ability.form-control')) { const bdOptions = document.querySelectorAll('#bd-form select#ability.form-control > option'); const bdSelectedIndex = [...bdOptions].findIndex(e => e.value === document.querySelector('#bd-form select#ability.form-control').value); const bdOptionsLength = bdOptions.length; if (arrowKeyCount === 2 || arrowKeyCount === 3) { //right || up ("previous one") bdOptions[(bdSelectedIndex - 1 + bdOptionsLength) % bdOptionsLength].selected = true; //previous item or loop to the bottom } else if (arrowKeyCount === 1 || arrowKeyCount === 4) { //left || down ("next one") bdOptions[(bdSelectedIndex + 1) % bdOptionsLength].selected = true; //next item or loop to the top } else console.log("error in arrowKeyCount"); } else if (location.pathname.match(/tyranuevavu/)) { const evLower = document.querySelector('input[value="lower"]'); const tyHigher = document.querySelector('input[value="higher"]'); if (evLower && tyHigher) { if (arrowKeyCount === 2 || arrowKeyCount === 3) { tyHigher.click(); } else if (arrowKeyCount === 1 || arrowKeyCount === 4) { evLower.click(); } else console.log("error in arrowKeyCount"); } } else if (location.pathname.match(/cheat/)) { const letSlide = document.querySelector('input[value="Let Slide"]'); const accuseCheat = document.querySelector('input[value="Accuse of Cheating"]'); if (letSlide && accuseCheat) { //right/up arrow to accuse, left/down arrow to let slide if (arrowKeyCount === 2 || arrowKeyCount === 3) { accuseCheat.click(); } else if (arrowKeyCount === 1 || arrowKeyCount === 4) { letSlide.click(); } else console.log("error in arrowKeyCount"); } } else if (location.pathname.match(/kiosk/)) { const kioskSCOptions = document.querySelectorAll(".select-wrapper select[name='card'] > option"); const kioskSCSelectedIndex = [...kioskSCOptions].findIndex(e => e.selected === true); const kioskSCLength = kioskSCOptions.length; if (arrowKeyCount === 2 || arrowKeyCount === 3) { //right || up ("previous one") kioskSCOptions[(kioskSCSelectedIndex - 1 + kioskSCLength) % kioskSCLength].selected = true; //previous item or loop to the bottom } else if (arrowKeyCount === 1 || arrowKeyCount === 4) { //left || down ("next one") kioskSCOptions[(kioskSCSelectedIndex + 1) % kioskSCLength].selected = true; //next item or loop to the top } else console.log("error in arrowKeyCount"); } break; // Number Keys (above QWERTY) case "Digit0": digitKeyCount++; //falls through case "Digit9": digitKeyCount++; //falls through case "Digit8": digitKeyCount++; //falls through case "Digit7": digitKeyCount++; //falls through case "Digit6": digitKeyCount++; //falls through case "Digit5": digitKeyCount++; //falls through case "Digit4": digitKeyCount++; //falls through case "Digit3": digitKeyCount++; //falls through case "Digit2": digitKeyCount++; //falls through case "Digit1": if (location.pathname.match(/witchtower|braintree|esophagor|kitchen|snowfaerie|employ|darkfaerie/)) { if (questSidebarOverride && typeof questOverrideActions[digitKeyCount] != 'undefined') { let keyCheck = parseInt(Object.keys(questOverrideActions[digitKeyCount])) - 1; if (digitKeyCount === keyCheck) window.location.assign(location.origin + Object.values(questOverrideActions[digitKeyCount]).toString()); else {console.log("Your quest order settings are incorrect, please check them!"); break;} } else if (!questSidebarOverride && digitKeyCount < document.querySelector(`div#aio_sidebar > .quests > .aioImg`).childElementCount) { let rankOrderList = [...document.querySelectorAll('.quests .aioImg div')].sort((a, b) => a.style.order - b.style.order); rankOrderList[digitKeyCount].firstChild.click(); } else if (questSidebarOverride && typeof questOverrideActions[digitKeyCount] == 'undefined') { console.log("You didn't assign anything to that key.");} } else if (location.pathname.match(/dome\/1p/)) { let bdEquips = document.querySelectorAll('#bd-form table input[type="checkbox"]'); if (bdEquips && digitKeyCount < bdEquips.length) { let checkbox = bdEquips[digitKeyCount]; checkbox.checked = !checkbox.checked; checkbox.dispatchEvent(new Event('change', {bubbles: true})); } } else if (location.pathname.match(/scratchcard/)) { let scNumSpot = document.querySelector(`#scratchcard a[href*="tile=${digitKeyCount}"]:not(:has(img))`); if (scNumSpot) scNumSpot.click(); } else if (location.pathname.match(/bilgedice/)) { bilgeDice(digitKeyCount); } else if (location.pathname.match(/slots/)) { scorchySlots(digitKeyCount); } else if (location.pathname.match(/pyramids/)) { pyramids(digitKeyCount); } break; // Miscellaneous Keys case "Numpad0": //close the usershop/SDB/SW search results/FC bet page/battlepedia with numpad 0 if (location.pathname.match(/market\/browseshop|safetydeposit|market\/wizard|battlepedia|foodclub\/current_bets/)) { window.close(); } break; // quick navigation for Dailies, Timelines, Quests, and Water(fishing) case "KeyD": //Dailies if (dKeyNavigation) { let dArr = [...document.querySelectorAll('div.dailies a')]?.sort((a, b) => parseInt(a.parentElement.style.order) - parseInt(b.parentElement.style.order)).map((e,i,arr) => { if (e.href.match(/turmaculus|games\/featured/)) {return} else return e; }).filter(Boolean); if (dArr[0]) {dArr[0].click();} } break; case "KeyT": //Timelies if (tKeyNavigation) { let tArr = [...document.querySelectorAll('div.timelies a')]?.sort((a, b) => parseInt(a.parentElement.style.order) - parseInt(b.parentElement.style.order)).map((e,i,arr) => { if (e.children[0].className.match(/aio-kad-fed/)) {return} else return e; }).filter(Boolean); if (location.pathname.match(/prehistoric\/wheel/)) { if (document.querySelector('input[value="Donate to Plesio"]')) tArr = tArr.map((e,i,arr) => {if (e.href.match(/prehistoric\/wheel/)) {return} else return e;}).filter(Boolean); } // let faveKiosk = 'winter'; if (tArr[0].href.match(/kiosk/)) {} else if (tArr[0]) {tArr[0].click();} } break; case "KeyQ": //Quests if (qKeyNavigation) { let qArr = [...document.querySelectorAll('div.quests a')]?.sort((a, b) => parseInt(a.parentElement.style.order) - parseInt(b.parentElement.style.order)); if (qArr[0]) {qArr[0].click();} } break; case "KeyW": //Water(fishing) if (wKeyNavigation) { const newCast = document.querySelector("a:has(img[title*='is ready to fish!'])"); if (newCast) newCast.click(); } break; case doItAllQuick: //Quick __ and __ with all if (location.pathname.match(/lottery/)) { const quickPick = document.querySelector("input[value='Quick Pick!']"); if (quickPick) quickPick.click(); } else if (location.pathname.match(/wishing/)) { const quickWish = document.querySelector("input[name='quick-wish']"); if (quickWish) {quickWish.click();} } else if (location.pathname.match(/water\/fishing/)) { const allFishing = document.querySelector("input[value='Fish with Everyone!']"); if (allFishing) allFishing.click(); } else if (location.pathname.match(/merrygoround/)) { const allMerryGoRound = document.querySelector("input[value^='Cheer Up']"); if (allMerryGoRound) allMerryGoRound.click(); } else if (location.pathname.match(/tikitours/)) { const allTikiTours = document.querySelector("button[name='all_pets']"); if (allTikiTours) allTikiTours.click(); } break; //choose strong/medium/weak for BD attacks case "KeyS": bdStrMedW++; //falls through case "KeyM": bdStrMedW++; //falls through case "KeyW": if (location.pathname.match(/dome\/1p/)) { const bdSMWOption = document.querySelector('#bd-form select#power.form-control'); if (bdSMWOption) {bdSMWOption.value = bdStrMedW === 2 ? 'strong' : bdStrMedW === 1 ? 'medium' : 'weak';} } break; } }); /////////////////// Bilge Dice (credit to Kait) async function bilgeDice (keyEntered) { //initialize our win streak async functions for GM.setValue and GM.getValue [synchronous GM_s/getValue are deprecated] const BDSTORE = 'bilgedice-winstreak'; const getBDWinStreak = async () => { return await GM.getValue(BDSTORE); } const bdWinStreak = await getBDWinStreak(); //initialize winstreak in case of error or new install if (typeof bdWinStreak === 'undefined') {GM.setValue(BDSTORE, "Current win streak: ???");} // initialize the checkboxes array, if possible const bdCheckboxes = document.querySelectorAll('input[id*="roll_"]'); // add win streak text and/or instruct regarding checkboxes but ONLY ONCE if (!document.querySelector('#page_content').textContent.includes("Current win streak")) { const bdWinStreakDiv = document.createElement('div'); bdWinStreakDiv.textContent = `${bdWinStreak}`; bdWinStreakDiv.setAttribute("style", "font-size: 12px; text-align: center;"); document.querySelector('#page_content').appendChild(bdWinStreakDiv); } if (!document.querySelector('#page_content').textContent.includes("(Use number keys to select or press spacebar to select all)") && bdCheckboxes.length) { const bdSpaceText = document.createElement('p'); bdSpaceText.setAttribute("style", "height: auto; font-size: 10px; width: auto; font-weight: bold; text-align: center;"); bdSpaceText.textContent= "(Use number keys to select or press spacebar to select all)"; document.querySelector("#bilge-dice-user-wrapper > form").insertAdjacentElement('afterend', bdSpaceText); } // on the front page, choose the ante automagically upon player pressing Enter let bilgeAnteCode = parseInt(bilgeAnteChoices.indexOf(bilgeAnteAmount)) + 1; if (!document.querySelector(`input[name="bet_${bilgeAnteCode}"]`) && document.querySelector(`input[name="bet_1"]`)) { bilgeAnteCode = 1; console.log("Defaulted to 10 NP bet, user selection does not exist"); } else if (document.querySelector(`input[name="bet_${bilgeAnteCode}"]`)) { if (keyEntered === 'Enter') { console.log("ante: " + bilgeAnteAmount + " NP"); document.querySelector(`input[name="bet_${bilgeAnteCode}"]`).click(); } } // on the front page, update the win streak and set it if possible if (location.pathname.match(/bilgedice/) && !location.pathname.match(/play/) && !location.pathname.match(/results/)) { const bdWinStreakElement = document.querySelector("#page_content div.center.mt-2 p"); const bdWinStreakTemp = bdWinStreakElement ? bdWinStreakElement.textContent : null; if (bdWinStreakTemp) { GM.setValue(BDSTORE, bdWinStreakTemp); console.log("stored bilgedice-winstreak", bdWinStreakTemp); } //if a number key is entered on this page, its relative ante position will be selected, if possible if (typeof keyEntered === 'number') { bilgeAnteCode = 1 + keyEntered; if (document.querySelector(`input[name="bet_${bilgeAnteCode}"]`)) { console.log("ante chosen: " + bilgeAnteChoices[keyEntered] + " NP"); document.querySelector(`input[name="bet_${bilgeAnteCode}"]`).click(); } } } // in the play window, handle checkboxes with number keys/spacebar and the rest with enter if (location.pathname.match(/bilgedice\/play/) || location.pathname.match(/bilgedice\/results/)) { if (typeof keyEntered === 'number' && keyEntered < bdCheckboxes.length) { bdCheckboxes[keyEntered].checked = !bdCheckboxes[keyEntered].checked; bdCheckboxes[keyEntered].dispatchEvent(new Event('change', { bubbles: true })); } else if (keyEntered === 'Space') { bdCheckboxes.forEach(checkbox => { checkbox.checked = !checkbox.checked; checkbox.dispatchEvent(new Event('change', { bubbles: true })); } ); } else if (keyEntered === 'Enter') document.querySelector('input[value="Keep"], input[value*="scallywags"], input[value*="Play Again"], input[value*="Play again"]').click(); } } /////////////////// Kiss The Mortog (credit to Kait) function kissTheMortog (keyEntered) { //initialize the mortogs array, if possible const mortogElements = document.querySelectorAll('#mortogs > div > form[action*="process_kissthemortog"]'); //on a win, check if player reached their selected prize pot value, alert them if so if (document.querySelector('input[value="Continue"]')) { let ktmPrizePot = parseInt(document.querySelector('main p.center:nth-of-type(2) strong').innerText.match(/(\d+) NP/)[1]); if (ktmPrizePot >= mortogLimit && !Boolean(keyEntered)) { alert("Prize limit reached! Consider cashing out at this time. Pressing Enter will continue playing."); document.querySelector('main form input.form-control[value*="Winnings"]').setAttribute("style", "background-color: IndianRed;"); } } //process enter key functions if (keyEntered === 'Enter') { //if there are mortogs, choose one based on user preferences if (mortogElements.length) { //random choice if (mortogFavorite === "random") { randomMortog(); //choose only first mortogs } else if (mortogFavorite === "first") { console.log("user prefers first mortog, chose mortog[0]"); mortogElements[0].submit(); //choose only last mortogs } else if (mortogFavorite === "last") { let last = mortogElements.length-1; console.log("user prefers last mortog, chose mortog[" + last + "]"); mortogElements[last].submit(); //user selection did not compute, choose randomly } else { console.log("error in mortogFavorite selection, defaulting to random"); randomMortog(); } //otherwise, select the play/next button } else document.querySelector('input[value="Continue"], input[value*="Try again"]').click(); } //since we want to choose a random mortog in two circumstances, make it into a function function randomMortog () { const randomIndex = Math.floor(Math.random() * mortogElements.length); console.log("randomly chose mortog[" + randomIndex + "]"); mortogElements[randomIndex].submit(); } } /////////////////// Scorchy Slots (credit to Kait) async function scorchySlots (keyEntered) { //initialize our collective winnnings async functions for GM.setValue and GM.getValue [synchronous GM_s/getValue are deprecated] const SSSTORE = 'scorchyslots-winnings'; const getSSWinnings = async () => { return JSON.parse(await GM.getValue(SSSTORE, '{}')); } const ssWinnings = await getSSWinnings(); // add winnings text but ONLY ONCE if (!document.querySelector('#page_content').textContent.includes("Session winnings")) { const ssWinningsDiv = document.createElement('div'); ssWinningsDiv.textContent = `${ssWinnings.session} / ${ssWinnings.total}\nDon't forget to go back to Scorchy Slots Home to update your total winnings after this session!`; ssWinningsDiv.setAttribute("style", "text-align: center; font-size: 10px; white-space: pre-wrap;"); document.querySelector('#page_content').appendChild(ssWinningsDiv); } //check if a random event is happening now let randEvent = document.querySelector("#page_event div.re_text"); let ssRENP = 0; //set the amount of NP it awarded/took away to 0 as default if (randEvent) {ssRENP = ssRandEvent(randEvent);} //run the random event function if an event did happen, which will return a +/- value of NP if applicable //initialize NP on hand for tabulation of session NP changes let npOnHand = parseInt(document.querySelector("#userinfo #on-hand-np").textContent.toString().replace(/,/g, "")); //initialize storage of winnings in case of error or new install if (typeof ssWinnings.total === 'undefined' && !ssWinnings.total) { let tempNewSSWinnings = {total: `Total winnings: 0 NP`, session: `Session winnings: 0 NP`, onhand: npOnHand}; console.log("set scorchyslots-winnings", tempNewSSWinnings); GM.setValue(SSSTORE, JSON.stringify(tempNewSSWinnings)); } //on loading the main Scorchy Slots page, pass session winnings (or losses) into total, reset session value to 0 if (location.pathname.match(/games\/slots/)) { let tempWinSession = parseInt(/(?<=gs: )([\d,+-]+)(?= NP)/.exec(ssWinnings.session)[0].replace(/,/g, "")); let tempWinTotal = parseInt(/(?<=gs: )([\d,+-]+)(?= NP)/.exec(ssWinnings.total)[0].replace(/,/g, "")); let tempWinnings = Intl.NumberFormat("en-US", {signDisplay: "exceptZero"}).format(tempWinSession + tempWinTotal + ssRENP); let tempNewSSWinnings = {total: `Total winnings: ${Intl.NumberFormat("en-US", {signDisplay: "exceptZero"}).format(tempWinnings)} NP`, session: `Session winnings: 0 NP`, onhand: npOnHand}; console.log("set scorchyslots-winnings", tempNewSSWinnings); GM.setValue(SSSTORE, JSON.stringify(tempNewSSWinnings)); } //initialize the checkboxes hold array, if possible const scorchyCheckboxes = document.querySelectorAll('#scorchy-hold input[name*="scorchy_hold_"]'); //when the user is prompted to hold... if (scorchyCheckboxes.length) { //create the element containing instructions regarding checkboxes but ONLY ONCE if (!document.querySelector('#page_content').textContent.includes("Select which reels to hold")) { const ssSpaceText = document.createElement('p'); ssSpaceText.setAttribute("style", "height: auto; font-size: 10px; width: auto; font-weight: bold; text-align: center;"); ssSpaceText.textContent= "Select which reels to hold using number keys 1-4 or spacebar to select all"; document.querySelector("#scorchy-inner-wrapper > div#scorchy-hold.scorchy-row").insertAdjacentElement('beforebegin', ssSpaceText); } //if a number key is pressed, select that # hold slot if (typeof keyEntered === 'number' && keyEntered < scorchyCheckboxes.length) { scorchyCheckboxes[keyEntered].checked = !scorchyCheckboxes[keyEntered].checked; scorchyCheckboxes[keyEntered].dispatchEvent(new Event('change', { bubbles: true })); //if spacebar is pressed, select all } else if (keyEntered === 'Space') { scorchyCheckboxes.forEach(checkbox => { checkbox.checked = !checkbox.checked; checkbox.dispatchEvent(new Event('change', { bubbles: true })); }); } } //handle enter key usage if (keyEntered === 'Enter') { //while on the play page... if (location.pathname.match(/play_slots/)) { //...update the session NP based on values just prior to navigating to the next page (with enter) let tempSessionWinnings = parseInt(/(?<=gs: )([\d,+-]+)(?= NP)/.exec(ssWinnings.session)[0].replace(/,/g, "")); let onHandTemp = ssWinnings.onhand; let ssSessionTemp = npOnHand - onHandTemp + tempSessionWinnings + ssRENP; console.log(npOnHand, onHandTemp, tempSessionWinnings, ssSessionTemp); let tempNewSSWinnings = {total: ssWinnings.total, session: `Session winnings: ${Intl.NumberFormat("en-US", {signDisplay: "exceptZero"}).format(ssSessionTemp)} NP`, onhand: npOnHand}; console.log("set scorchyslots-winnings", tempNewSSWinnings); GM.setValue(SSSTORE, JSON.stringify(tempNewSSWinnings)); } //this keeps values from updating with every checkbox entry document.querySelector('input[value="Click Here to Play"], input[value="Collect Winnings"], input[value="Play Again"]').click(); } //handle random events that might occur on Scorchy Slots pages that affect NP values [WIP, still collecting text for applicable events] function ssRandEvent (randEvent) { let reNP = 0; reNP = randEvent?.innerText?.match(/([\+d,]+)(?= N?n?eopoints| NP| np)/)[0]?.replace(/,/g, ""); if (reNP) { if (/gives you|[Ff]+ortune|[Tt]+reasure|spilled/.test(randEvent.innerText)) {} else if (/steal|appropriate|take|lose/.test(randEvent.innerText)) reNP = 0-reNP; return reNP; } } } /////////////////// Snow Wars (credit to Dij & Kait) async function snowWars (keyEntered) { const SWSTORE = 'snowwars-memory'; const getSWMemory = async () => { return JSON.parse(await GM.getValue(SWSTORE, '{}')); } const swMemory = await getSWMemory(); var swClickSpots, swIndex; const swBoard = document.querySelector("div.snowwars-board-container.flex"); if (swBoard && !swClickSpots) {swClickSpots = [...swBoard.querySelectorAll("div.snowwars-spot:not(.snowwars-axis) > a")].reduce((acc, cur) => ({...acc, [cur.outerHTML.match(/(?<=Attack\(event, ')[\d]+(?='\)\")/)[0]]: cur}), 0);} //initialize storage with new game, use 0 as first cell when enter is pressed if (document.querySelector('#page_content').textContent.includes("Welcome to Snow Wars") || typeof swMemory.lastcell === 'undefined') { console.log("storing",JSON.stringify({lastcell: 0, total: 0, bookmark: 0})); await GM.setValue(SWSTORE, JSON.stringify({lastcell: 0, total: 0, bookmark: 0})); } //handle Enter key if (keyEntered === 'Enter' && swClickSpots) { if (swClickSpots[swMemory.lastcell]) swClickSpots[swMemory.lastcell].click(); //else swBoard.querySelector(`div.snowwars-board-container.flex > a[onclick='${swMemory.lastcell}']`).click(); } else if (keyEntered === 'Enter') { document.querySelector("input[value='Continue Game'], input[name='start_round']").click(); } //on the gameboard page... if (!keyEntered && swBoard && !swClickSpots[swMemory.lastcell]) { swIndex = swMemory.lastcell; if (swMemory.lastcell != swMemory.bookmark) swIndex = swMemory.bookmark; //find next play based on checkerboard grid while (!swClickSpots[swIndex] && swIndex < 48) { swIndex = selectNextNum(swIndex); } if (swIndex >= 48) { swIndex = 1; while (!swClickSpots[swIndex] && swIndex < 48) { swIndex = selectNextNum(swIndex); } } console.log("found next spot", JSON.stringify(swIndex)); console.log("storing",JSON.stringify({lastcell: swIndex, total: swMemory.total, bookmark: swIndex})); await GM.setValue(SWSTORE, JSON.stringify({lastcell: swIndex, total: swMemory.total, bookmark: swIndex})); } //on the "results page... if (!keyEntered && !swBoard && document.querySelector("#page_content").textContent.includes("You fire at")) { //store temporary values of the memory values for manipulation if needed let tempTotal = 1 + swMemory.total; let tempLast = swMemory.lastcell; //check if the user clicked a value that was different from the one intended let crossCheck = document.querySelector("#page_content").textContent.match(/(?<=You fire at )[A-F][0-9]+(?= - )/)[0].split(""); let crossCheckCoord = (crossCheck[0].charCodeAt(0) - 65) * 8 + parseInt(crossCheck[1]) - 1; if (crossCheckCoord != swMemory.lastcell) {console.log("You clicked "+crossCheckCoord+" instead of my pick of "+swMemory.lastcell); tempLast = crossCheckCoord; } //if a hit was recorded, activate the flag to process the next spot if (document.querySelector("img[src*='hit.gif']")) { console.log("hit! coord: " + JSON.stringify(tempLast)); } console.log("storing7",JSON.stringify({lastcell: tempLast, total: tempTotal, bookmark: swMemory.bookmark})); await GM.setValue(SWSTORE, JSON.stringify({lastcell: tempLast, total: tempTotal, bookmark: swMemory.bookmark})); } //make a checkerboard pattern on the gameboard function selectNextNum (index) { if (index % 8 == 7) {return index + 1;} //last of row, move to first of next row else if (index % 8 == 6) {return index + 3;} //one before end of row, move to one after start of next row else {return index + 2;} //otherwise, every other } } /////////////////// Pyramids (credit to Shiba) function pyramids (keyEntered) { const pyrCardStorage = { 'key': 'pyramids-counts', 'get': function(suit) { const counts = JSON.parse(localStorage?.getItem(this.key)) || {}; return suit ? counts[suit] : counts; }, 'set': function(suit,cards) { const counts = pyrCardStorage.get(); console.log("setter", counts, suit, cards); counts = Object.assign(counts[suit],{[suit]: cards.slice()}); localStorage.setItem(this.key, JSON.stringify(counts)); }, 'reset': function() { let cards = ['2','3','4','5','6','7','8','9','10','J','Q','K','A']; let suits = ['clubs','diamonds','hearts','spades']; let deck = Object.assign(...suits.map((k, i) => ({[k]: cards.slice()}))); localStorage.setItem(this.key, JSON.stringify(deck)); }, 'del': function(suit,card) { const deck = pyrCardStorage.get(); if (deck[suit].includes(card)) { deck[suit].splice(deck[suit].indexOf(card),1); } localStorage.setItem(this.key, JSON.stringify(deck)); }, } const newGame = document.querySelector('input[value="Play Pyramids!"]'); const ongoing = document.querySelector('input[class="btn-link"]'); window.addEventListener("load", (event) => { if (ongoing) { const counts = pyrCardStorage.get(); //count clickable face-up pyramid cards let clickableFaceUp = [...document.querySelectorAll('img.face_up.clickable')].reduce((acc, e) => { let [_, num, suit] = /(?<=solitaire\/)(\d+)(?:_)(spades|hearts|clubs|diamonds)(?:.gif)/.exec(e.src) || []; if (num) acc[suit] ||= []; acc[suit].push(num > 10 ? ['J', 'Q', 'K', 'A'][num - 11] || num : num); return acc;}, {}); Object.entries(clickableFaceUp).forEach(([suit, cards]) => cards.forEach(card => pyrCardStorage.del(suit, card))); //count face-up draw pile cards let drawFaceUp = /(?<=solitaire\/)(\d+)(?:_)(spades|hearts|clubs|diamonds)(?:.gif)/.exec(document.querySelector('img.hand.face_up').src); if (drawFaceUp) pyrCardStorage.del(drawFaceUp[2], drawFaceUp[1] > 10 ? ['J', 'Q', 'K', 'A'][drawFaceUp[1] - 11] || drawFaceUp[1] : drawFaceUp[1]); //sum card counts const cardCountsSum = {"2": 0,"3": 0,"4": 0,"5": 0,"6": 0,"7": 0,"8": 0,"9": 0,"10": 0,"J": 0,"Q": 0,"K": 0,"A": 0}; for (const suit in counts) { counts[suit].forEach(card => { if (cardCountsSum.hasOwnProperty(card)) { cardCountsSum[card]++; } });} //add table if ($('#pyramids_counts').length && !$('#card-count-grid').length) { $('<style>').text(`#card-count-grid {display: table; width: inherit; background-color: var(--bgcolor);} #card-count-grid td {border-left: 1px solid white; border-top: 1px solid white;} #card-count-grid table {border-spacing: 0; width: inherit;}`).appendTo('head'); let tableHTML = `<span class="flex center-items justify-center no-right-border" id="card-count-grid"><table><tr>` + Array.from({length: 13}, (_, i) => `<td style="font-weight: bolder; font-style: italic;" id='${i}'>${Object.keys(cardCountsSum)[i]}</td>`).join('') + `</tr><tr>` + Array.from({length: 13}, (_, i) => { let condStyle = ''; if (Object.values(cardCountsSum)[i] === 2) { condStyle = 'background-color: #3a4b23; '; } else if (Object.values(cardCountsSum)[i] === 1) { condStyle = 'background-color: #8f6d2a; '; } else if (Object.values(cardCountsSum)[i] === 0) { condStyle = 'background-color: #84422a; '; } return `<td style="${condStyle}font-family: math;">${Object.values(cardCountsSum)[i]}</td>`; }).join('') + `</tr></table></span>`; $('#pyramids_counts').append(tableHTML); } } if (newGame) { pyrCardStorage.reset(); console.log("pyrCardStorage reset"); } }); let pClickCards = document.querySelectorAll('img.face_up[onclick="click_card(this)"]'); let pClickSort = [...pClickCards].sort((a,b) => a.className.match(/(?<=card_)(\d)+(?= )/)[0] - b.className.match(/(?<=card_)(\d)+(?= )/)[0]); if (typeof keyEntered === 'number' && keyEntered < pClickSort.length) { pClickSort[keyEntered].click(); } if (keyEntered === 'Enter') { let pClickDeck = document.querySelector('img.deck.clickable[onclick="click_card(this)"]'); let pCollect = document.querySelector('input[value="Collect Winnings"]'); let pWin = document.querySelector('input[value="Automatically Finish & Collect Winnings"]'); let pPlayAgain = document.querySelector('input[value="Play Pyramids Again!"]'); if (pClickDeck) pClickDeck.click(); else if (pWin) pWin.click(); else if (pCollect && document.querySelector('#page_content').textContent.includes("You do not have any draws left")) { if (confirm("Are you sure you want to end the game?")) {pCollect.click();} else {alert("The game will continue.");} } else if (newGame) newGame.click(); else if (pPlayAgain) pPlayAgain.click(); } } /////////////////// Tyranu Evavu (credit to Sanjix) function tyranuEvavu () { const teCardStorage = { 'key': 'tyranuevavu-counts', 'get': function(suit) { const counts = JSON.parse(localStorage?.getItem(this.key)) || {}; return suit ? counts[suit] : counts; }, 'set': function(suit,cards) { const counts = teCardStorage.get(); counts = Object.assign(counts[suit],{[suit]: cards.slice()}); localStorage.setItem(this.key, JSON.stringify(counts)); }, 'reset': function() { let cards = ['2','3','4','5','6','7','8','9','10','J','Q','K','A']; let suits = ['clubs','diamonds','hearts','spades']; let deck = Object.assign(...suits.map((k, i) => ({[k]: cards.slice()}))); localStorage.setItem(this.key, JSON.stringify(deck)); console.log("reset tyranuevavu-counts"); }, 'del': function(suit,card) { const deck = teCardStorage.get(); if (deck[suit].includes(card)) { deck[suit].splice(deck[suit].indexOf(card),1); } localStorage.setItem(this.key, JSON.stringify(deck)); }, } const newGame = document.querySelector('input[value="Play Now!"]'); const playAgain = document.querySelector('input[value="Play Again"]'); const evLower = document.querySelector('input[value="lower"]'); const tyHigher = document.querySelector('input[value="higher"]'); window.addEventListener("load", (event) => { if (evLower && tyHigher) { const counts = teCardStorage.get(); //<img src="https://grundoscafe.b-cdn.net/games/php_games/tyranuevavu/4_spades.gif"> let drawPlayCard = /(?<=tyranuevavu\/)(\d+)(?:_)(spades|hearts|clubs|diamonds)(?:.gif)/.exec(document.querySelector('div.te-cards img').src); if (drawPlayCard) teCardStorage.del(drawPlayCard[2], drawPlayCard[1] > 10 ? ['J', 'Q', 'K', 'A'][drawPlayCard[1] - 11] || drawPlayCard[1] : drawPlayCard[1]); //sum card counts const cardCountsSum = {"2": 0,"3": 0,"4": 0,"5": 0,"6": 0,"7": 0,"8": 0,"9": 0,"10": 0,"J": 0,"Q": 0,"K": 0,"A": 0}; for (const suit in counts) { counts[suit].forEach(card => { if (cardCountsSum.hasOwnProperty(card)) { cardCountsSum[card]++; } });} var directionElement = Object.assign(document.createElement('p'), { className: 'te-directions' }); let smallerCards = []; let biggerCards = []; const cardFaceOrder = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"]; const cardNumberOrder = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14"]; const drawnCardValue = drawPlayCard[1]; for (const card in cardCountsSum) { if (cardFaceOrder.indexOf(card) < cardNumberOrder.indexOf(drawnCardValue)) { let temp = Array(cardCountsSum[card]); temp.fill(card); smallerCards = smallerCards.concat(temp); } else if (cardFaceOrder.indexOf(card) > cardNumberOrder.indexOf(drawnCardValue)) { let temp = Array(cardCountsSum[card]); temp.fill(card); biggerCards = biggerCards.concat(temp);} } console.log("lower: " + smallerCards.length + " / higher: " + biggerCards.length); let probTE; if (smallerCards.length > biggerCards.length) { probTE = (smallerCards.length)/(smallerCards.length + biggerCards.length); directionElement.textContent = `${Intl.NumberFormat("en-US", {style: "percent"}).format(probTE)} chance it's Evavu/Lower`; } else if (smallerCards.length < biggerCards.length) { probTE = (biggerCards.length)/(biggerCards.length + smallerCards.length); directionElement.textContent = `${Intl.NumberFormat("en-US", {style: "percent"}).format(probTE)} chance it's Tyranu/Higher`; } else { directionElement.textContent = 'Either'; } if (!document.querySelector('p.te-directions')) { document.querySelector('.te-buttons').prepend(directionElement); } } else if (newGame) { teCardStorage.reset(); } }); } /////////////////// Cheat (credit to Sanjix) function cheat () { const chtCardStorage = { 'key': 'cheat-counts', 'get': function(type) { const counts = JSON.parse(localStorage?.getItem(this.key)) || {}; return type ? counts[type] : counts; }, 'set': function(type,ele) { const counts = chtCardStorage.get(); counts[type] = ele; localStorage.setItem(this.key, JSON.stringify(counts)); }, 'reset': function() { let deck = Object.fromEntries(['clubs', 'diamonds', 'hearts', 'spades'].map(suit => [suit, []])); let counts = { deck, plays: [], sums: {}, pile: {} }; ['2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K', 'A'].forEach(card => { counts.sums[card] = counts.pile[card] = 0;}); localStorage.setItem(this.key, JSON.stringify(counts)); console.log("reset cheat-counts"); }, //deck management // add player's cards to the deck 'addDeck': function(suit,card) { const counts = chtCardStorage.get(); const deck = counts.deck; if (deck[suit].includes(card)) {} else { deck[suit] = deck[suit].concat(card); } localStorage.setItem(this.key, JSON.stringify(counts)); chtCardStorage.sumDeck(); }, // remove player's cards from the deck when played 'delDeck': function(suit,card) { const counts = chtCardStorage.get(); const deck = counts.deck; if (deck[suit].includes(card)) { deck[suit].splice(deck[suit].indexOf(card),1); } localStorage.setItem(this.key, JSON.stringify(counts)); chtCardStorage.sumDeck(); }, // sum up players cards by value 'sumDeck': function() { const counts = chtCardStorage.get(); const sums = {...Object.fromEntries([...Array(9).keys()].map(i => [i + 2, 0])), "J": 0, "Q": 0, "K": 0, "A": 0 }; const deck = counts.deck; for (const suit in deck) { deck[suit].forEach(card => { if (sums.hasOwnProperty(card)) { sums[card]++; } });} counts.sums = sums; localStorage.setItem(this.key, JSON.stringify(counts)); }, //pile management // add played cards to the pile (player and npc) 'addPile': function(cards) { const counts = chtCardStorage.get(); const pile = counts.pile; for (const card in cards) { pile[card] = pile[card] + cards[card]; } localStorage.setItem(this.key, JSON.stringify(counts)); }, // reset the pile 'delPile': function() { const counts = chtCardStorage.get(); const pile = {...Object.fromEntries([...Array(9).keys()].map(i => [i + 2, 0])), "J": 0, "Q": 0, "K": 0, "A": 0 }; counts.pile = pile; localStorage.setItem(this.key, JSON.stringify(counts)); }, // build a p element displaying the pile's contents 'printPile': function() { const counts = chtCardStorage.get(); const pile = counts.pile; var pileContentsP = document.createElement('p'); pileContentsP.setAttribute("style", "text-align: center; font-size: 10px; margin-block-start: 0px; margin-block-end: 0px;"); var pileCards = []; let keymap = {'2':'Twos', '3':'Threes','4':'Fours', '5':'Fives','6':'Sixes', '7':'Sevens','8':'Eights','9':'Nines','10':'Tens','J':'Jacks', 'Q':'Queens','K':'Kings','A':'Aces'}; for (const card in pile) { if (pile[card] != 0) pileCards += ` ${pile[card]}x ${keymap[card]} |`; } pileCards = pileCards.length ? pileCards.substring(0,pileCards.length-2) : " empty"; pileContentsP.innerHTML = "Pile:" + pileCards; return pileContentsP; }, //play management // add a play to the log 'addPlay': function(player, cards, accusedby, innocence, turn, as) { const counts = chtCardStorage.get(); const plays = counts.plays; let lastPlayer, lastCards, lastTurn, lastAs; if (plays.length > 0) { lastPlayer = new String(plays.at(-1).player); lastCards = new String(plays.at(-1).cards); lastTurn = plays.at(-1).turn ? parseInt(new String(plays.at(-1).turn)) : 1; lastAs = plays.at(-1).as ? new String(plays.at(-1).as) : null; } var tempPlay = new Object; tempPlay.player = player ? player : null; tempPlay.cards = cards ? cards : null; tempPlay.accusedby = accusedby ? accusedby : null; tempPlay.innocence = innocence ? innocence : null; tempPlay.turn = turn ? 1+turn : 1+lastTurn; tempPlay.as = as ? as : lastAs; plays.push(tempPlay); counts.plays = plays; if (plays.length == 1 || (lastPlayer != tempPlay.player && lastCards != tempPlay.cards)) { localStorage.setItem(this.key, JSON.stringify(counts)); } }, // edit the most recent play in the log 'editPlay': function(player, cards, accusedby, innocence, turn, as) { const counts = chtCardStorage.get(); var plays = counts.plays; if (plays?.at(-1).player === player && (!plays.at(-1).cards || Object.keys(plays?.at(-1).cards)[0] === Object.keys(cards)[0])) { plays.at(-1).player = player; plays.at(-1).cards = plays.at(-1).cards ? plays.at(-1).cards : cards; plays.at(-1).accusedby = accusedby ? accusedby : null; plays.at(-1).innocence = innocence ? innocence : null; plays.at(-1).turn = turn ? turn : 1; if (plays?.at(-1).player === "You") { let keymap; if (Object.values(plays?.at(-1).cards)[0] === 1) { keymap = {'2':'a Two','3':'a Three','4':'a Four','5':'a Five','6':'a Six', '7':'a Seven','8':'an Eight','9':'a Nine','10':'a Ten','Jack':'a Jack', 'Queen':'a Queen','King':'a King','Ace':'an Ace'};} else { keymap = {'2':'Twos','3':'Threes','4':'Fours','5':'Fives','6':'Sixes', '7':'Sevens','8':'Eights','9':'Nines','10':'Tens','Jack':'Jacks', 'Queen':'Queens','King':'Kings','Ace':'Aces'};} console.log("keymap",Boolean(keymap[as])); if (keymap[as]) {plays.at(-1).as = keymap[as];} else { plays.at(-1).as = plays.at(-1).as ? plays.at(-1).as : as; } } else { plays.at(-1).as = plays.at(-1).as ? plays.at(-1).as : as; } } counts.plays = plays; localStorage.setItem(this.key, JSON.stringify(counts)); }, // build a div element containing the play log 'printPlay': function() { const counts = chtCardStorage.get(); const plays = counts.plays; var exceptLatest = plays.slice(0,plays.length - 1); var playHistoryDiv = document.createElement('div'); let keymap; exceptLatest.forEach((play) => { let cur = document.createElement('p'); let oval = Object.values(play.cards)[0]; if (oval === 1) {keymap = {'2':'Two', '3':'Three','4':'Four', '5':'Five','6':'Six', '7':'Seven','8':'Eight','9':'Nine','10':'Ten','J':'Jack', 'Q':'Queen','K':'King','A':'Ace'}} else {keymap = {'2':'Twos', '3':'Threes','4':'Fours', '5':'Fives','6':'Sixes', '7':'Sevens','8':'Eights','9':'Nines','10':'Tens','J':'Jacks', 'Q':'Queens','K':'Kings','A':'Aces'}} let okey = keymap[Object.keys(play.cards)[0]]; cur.innerHTML = `Turn ${play.turn}: ` + play.player + ` played <b>${oval}x ${okey}</b>`; if (play.player == 'You') {cur.innerHTML += ` passed off as ` + play.as;} cur.innerHTML += `.`; let pronounObj = play.player == 'You' ? 'you' : 'them'; let pronounSub = play.player == 'You' ? 'you' : 'they'; if (play.accusedby) { cur.innerHTML += ' ' + play.accusedby + ' '; if (play.innocence) { cur.innerHTML += 'accused ' + pronounObj + ' but ' + pronounSub + ' weren\'t cheating!'; } else { cur.innerHTML += 'caught ' + pronounObj + ' cheating!'; } } else { cur.innerHTML += ' No one accused ' + pronounObj + ' of cheating.'; } playHistoryDiv.prepend(cur); }); return playHistoryDiv; } } //put the main page text content into a constant for use later const chtContent = document.querySelector('#page_content > main')?.textContent; //these are various buttons to start playing const newGame = document.querySelector('input[value="Play ??"]'); //???????? const playAgain = document.querySelector('main input[value="Play Again"]'); const resumePlay = document.querySelector('main input[value="Return to Game"]'); const nextDiff = document.querySelector('input[value^="Continue to Difficulty"]'); //button that appears when it is the player's turn to choose cards const myTurn = document.querySelector('main input[value="Go!"]'); //buttons that appear when other players are playing const letSlide = document.querySelector('input[value="Let Slide"]'); const accuseCheat = document.querySelector('input[value="Accuse of Cheating"]'); const playResolve = document.querySelector('input[value="Click to Continue"]'); //DOM elements where we will insert our own elements const pileP = document.querySelector('#cheat-cast + p'); //pileInt = parseInt(document.querySelector('#cheat-cast + p > b')?.textContent); const returnDiv = document.querySelector('#cheat-cast ~ #cards ~ p.center ~ div.button-group'); //reset if everyone has 13 cards or you can increase your difficulty let npcsCardCounts = [...document.querySelectorAll('#cheat-cast .cheat-player b')]?.map(e => +e.textContent), myCardCount = +document.querySelector("main > p.center.red + p.center > b")?.textContent; if (myCardCount && new Set([...npcsCardCounts, myCardCount, 13]).size === 1) {chtCardStorage.reset();} else if (nextDiff) {chtCardStorage.reset();} //on window load... window.addEventListener("load", (event) => { var counts = chtCardStorage.get(); //if it's the player's turn, put the current hand into deck, reset sums, display known cards if (myTurn) { let deck = counts.deck; let turn = counts.plays.length>0 ? counts.plays.at(-1).turn : 0; let as = counts.plays.length>0 ? counts.plays.at(-1).as : null; let myHand = [...document.querySelectorAll('#cards div.card img[onclick*="toggle_card"]')].reduce((acc, e) => { let [_, num, suit] = /(?<=cards\/)(\d+)(?:_)(spades|hearts|clubs|diamonds)(?:.gif)/.exec(e.src) || []; if (num) acc[suit] ||= []; acc[suit].push(num > 10 ? ['J', 'Q', 'K', 'A'][num - 11] || num : num); return acc;}, {}); Object.entries(myHand).forEach(([suit, cards]) => cards.forEach(card => chtCardStorage.addDeck(suit, card))); //make a play entry chtCardStorage.addPlay("You", null, null, null, turn, as); //refresh storage counts = chtCardStorage.get(); //build display elements returnDiv.after(chtCardStorage.printPlay()); pileP.after(chtCardStorage.printPile()); //if it's someone else's turn to play, record the play, build the pile } else if (letSlide || accuseCheat) { counts = chtCardStorage.get(); let turn = counts.plays.at(-1).turn; let as = counts.plays.at(-1).as; const parsed = document.querySelector('#cards + p strong')?.textContent.match(/(.*?) Played (\d) (\w+)$/); const cardMap = { 'Two': '2', 'Twos': '2', 'Three': '3', 'Threes': '3', 'Four': '4', 'Fours': '4', 'Five': '5', 'Fives': '5', 'Six': '6', 'Sixes': '6', 'Seven': '7', 'Sevens': '7', 'Eight': '8', 'Eights': '8', 'Nine': '9', 'Nines': '9', 'Ten': '10', 'Tens': '10', 'Jack': 'J', 'Jacks': 'J', 'Queen': 'Q', 'Queens': 'Q', 'King': 'K', 'Kings': 'K', 'Ace': 'A', 'Aces': 'A' }; chtCardStorage.addPlay(parsed[1], {[cardMap[parsed[3]]]: parseInt(parsed[2])}, null, null, turn, as); returnDiv.after(chtCardStorage.printPlay()); chtCardStorage.addPile({[cardMap[parsed[3]]]: parseInt(parsed[2])}); pileP.after(chtCardStorage.printPile()); counts = chtCardStorage.get(); //if it's the accusation resolution screen, manage it } else if (playResolve) { counts = chtCardStorage.get(); let tempPlayer = counts.plays.at(-1).player; let tempAccuser; let tempCards = counts.plays.at(-1).cards; let tempTurn = counts.plays.at(-1).turn; let tempAs = counts.plays.at(-1).as; let petName = document.querySelector("div#userinfo > a[href='/quickref/']").textContent; if (chtContent.includes("No one accused")) { let noOneAccused = chtContent.match(/No one accused ([\w ]+) of cheating./); if (noOneAccused[1] === tempPlayer || noOneAccused[1] === petName) { if (tempPlayer === petName) tempPlayer = "You"; chtCardStorage.editPlay(tempPlayer, tempCards, null, null, tempTurn, tempAs); } } else if (chtContent.match(/caught [\w ]+ cheating/)) { let caughtCheating = chtContent.match(/\b(.*) caught (.*?) cheating\b/); if (caughtCheating[2] === tempPlayer || caughtCheating[2] === petName) { tempAccuser = caughtCheating[1]; if (tempPlayer === petName) tempPlayer = "You"; if (tempAccuser === petName) tempAccuser = "You"; chtCardStorage.editPlay(tempPlayer, tempCards, tempAccuser, false, tempTurn, tempAs); chtCardStorage.delPile(); } } else if (chtContent.includes("was NOT CHEATING!!")) { let wasNotCheating = chtContent.match(/([\w ]+) accused ([\w ]+) of cheating,/); if (wasNotCheating[2] === tempPlayer || wasNotCheating[2] === petName) { tempAccuser = wasNotCheating[1]; if (tempPlayer === petName) tempPlayer = "You"; if (tempAccuser === petName) tempAccuser = "You"; chtCardStorage.editPlay(tempPlayer, tempCards, tempAccuser, true, tempTurn, tempAs); chtCardStorage.delPile(); } } } }); //when the player submits their turn, remove the selected cards from their deck, add them to the pile, and edit the play entry myTurn?.addEventListener("click", (event) => { var counts = chtCardStorage.get(); let mhPlayed = document.querySelector('input[id="cards_input"]')?.value; let turn = counts.plays.at(-1).turn ? counts.plays.at(-1).turn : 1; let asDropdown = document.querySelector('select[name="card_type"]')?.value; let tempCards = {}; document.querySelector('input[id="cards_input"]')?.value.split(",").forEach( card => { let parse = card.match(/(\d+)(?:_)(spades|hearts|clubs|diamonds)/); let face = parse[1] > 10 ? ['J', 'Q', 'K', 'A'][parse[1] - 11] || parse[1] : parse[1]; chtCardStorage.delDeck(parse[2], face); tempCards[face] = tempCards[face] ? 1+tempCards[face] : 1; }); chtCardStorage.addPile(tempCards); chtCardStorage.editPlay("You", tempCards, null, null, turn, asDropdown); }); } /////////////////// Grumpy/Wise King Avatar Selections function gwKing () { $("[onclick='generateJoke()']").trigger("click"); const questions = ['What','do','you do if','*Leave blank*','fierce','Grundos','*Leave blank*','has eaten too much', '*Leave blank*','tin of olives'].forEach((value, index) => {$(`#question${index + 1}`).val(value);}); $('#wisdom6').val('nugget'); } /////////////////// FC Bet Count (inspired by Senerio) function fcBetCount () { const fcBetCount = document.querySelectorAll('div[name="fc_bet_info"]').length; $('div.bg-darkred').text(`Current Bets (${fcBetCount})`); } /////////////////// Lottery Ticket Generator (inspired by neoquest_guide) async function lotteryTicketNumbers () { const ticketStorage = 'ticket-numbers'; const getTicketNumbers = async () => { return JSON.parse(await GM.getValue(ticketStorage, '{}')); } var ticketNumbers = await getTicketNumbers(); function setBallValues (numberArray) { document.querySelectorAll("div[class*='ball'] input[maxlength='2']").forEach((ball, index) => { ball.value = numberArray[index]; }); } let nthTicket = 0; if (document.querySelector("div[class='flex-column small-gap']").innerText.includes("why not buy one")) { generateNewTicketValues(20); ticketNumbers = await getTicketNumbers(); console.log("new tickets:", ticketNumbers); setBallValues(ticketNumbers[0]); } else if (document.querySelector("div[class='flex-column small-gap']").textContent.split("Ticket").length) { nthTicket = document.querySelector("div[class='flex-column small-gap']").textContent.split("Ticket").length - 1; } if (nthTicket < 20) { console.log("we need to purchase ticket number "+(nthTicket+1)+" with numbers: "+ticketNumbers[nthTicket]); setBallValues(ticketNumbers[nthTicket]); } else { console.log("daily ticket limit reached"); } // following source code from // https://www.andrew.cmu.edu/user/kmliu/neopets/lottery2.html function generateNewTicketValues (ticketsNeeded) { var arr = new Array(); for (var i=0; i<30; i++) {arr[i] = i+1;} let arr1, arr2, arr3, arr4, marr; arr1 = shuffle(arr); arr2 = leaf( arr1.slice(0,15), arr1.slice(15,30) ); arr2 = leaf( arr2.slice(0,15), arr2.slice(15,30) ); arr3 = leaf( arr2.slice(0,15), arr2.slice(15,30) ); arr3 = leaf( arr3.slice(0,15), arr3.slice(15,30) ); arr4 = leaf( arr3.slice(0,15), arr3.slice(15,30) ); arr4 = leaf( arr4.slice(0,15), arr4.slice(15,30) ); marr = arr1.concat(arr2,arr3,arr4); var newTickets = new Array(); for (i=0; i<ticketsNeeded; i++) { let temp = []; for (var j=0; j<6; j++) { temp.push(marr[6*i+j]); } temp.sort((a, b) => a - b); newTickets.push(temp); } console.log("generated "+ ticketsNeeded +" tickets"); GM.setValue(ticketStorage, JSON.stringify(newTickets)); } function shuffle (o) { for (var j, x, z = o.length; z; j = Math.floor(Math.random() * z), x = o[--z], o[z] = o[j], o[j] = x); return o; }; function leaf (a,b) { var res = new Array(); for(var k=0; k<a.length; k++) { res[k*2] = a[k]; res[k*2+1] = b[k]; } return res; }; } /////////////////// /////////////////// /////////////////// /////////////////// //references: // Z: https://gf.qytechs.cn/en/users/1257536-zzzzzooted // https://gf.qytechs.cn/en/scripts/486718-gc-questing-keyboard-controls/code // https://gf.qytechs.cn/en/scripts/499855-gc-bd-keyboard-mapping/code // Dij: https://gf.qytechs.cn/en/users/1272286-wreckstation // https://gf.qytechs.cn/en/scripts/499878-grundos-cafe-battledome-full-keyboard-controls/code // https://gf.qytechs.cn/en/scripts/497481-grundos-cafe-snow-wars-keyboard-controls/code // https://gf.qytechs.cn/en/scripts/489417-grundos-cafe-dice-a-roo-and-gormball-keyboard-controls/code // Kait: https://gf.qytechs.cn/en/users/1225524-kaitlin // https://gf.qytechs.cn/en/scripts/483608-gc-bilge-dice-keyboard-controls-tracking-enhancements/code // https://gf.qytechs.cn/en/scripts/487357-gc-kiss-the-mortog-keyboard-controls/code // https://gf.qytechs.cn/en/scripts/489543-gc-scorchy-slots-keyboard-mapping/code // https://gf.qytechs.cn/en/scripts/487432-gc-beta-dailies-global-preferences-wizard/code // https://gf.qytechs.cn/en/scripts/495558-gc-snow-wars-enhancements/code // Shiba: https://gf.qytechs.cn/en/users/1340979-shibashiba // https://gf.qytechs.cn/en/scripts/503746-shiba-s-pyramids-card-counter/code // Senerio: https://github.com/senerio // https://github.com/senerio/neopets-userscripts/blob/main/fcbetcount.user.js // neoquest_guide: https://neoquest.guide/userscripts/ // https://neoquest.guide/userscripts/lottery.user.js // Sanjix: https://gf.qytechs.cn/en/users/1175371-sanjix // https://gf.qytechs.cn/en/scripts/485673-gc-scratchcard-keyboard-controls/code // https://gf.qytechs.cn/en/scripts/475636-gc-tyranu-evavu-tracker/code // https://gf.qytechs.cn/en/scripts/475552-gc-tyranu-evavu-keyboard-controls/code // https://gf.qytechs.cn/en/scripts/507890-gc-cheat-helper/code // https://gf.qytechs.cn/en/scripts/508053-gc-cheat-keyboard-controls/code //to add: // Sanjix: https://gf.qytechs.cn/en/users/1175371-sanjix // https://gf.qytechs.cn/en/scripts/489924-gc-guess-the-card-keyboard-controls-test/code // https://gf.qytechs.cn/en/scripts/489923-gc-double-or-nothing-keyboard-controls/code // https://gf.qytechs.cn/en/scripts/485527-gc-pick-your-own-keyboard-controls/code // https://gf.qytechs.cn/en/scripts/486716-gc-shop-till-withdraw-keyboard-shortcut/code // Thank you all!! -mox ///////////////////
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址