// ==UserScript== // @name Cookie Monster Auto // @namespace https://github.com/fracergu // @version 0.1.0 // @description Auto-clicks golden cookies and the big cookie, and auto-buys beneficial upgrades and buildings using Cookie Monster's PP colour coding. Requires Cookie Monster. // @author Francisco Cerdán (fracergu) // @match https://orteil.dashnet.org/cookieclicker/* // @match https://cookieclicker.ee/* // @license MIT // @homepageURL https://github.com/fracergu/CookieMonsterAuto // @supportURL https://github.com/fracergu/CookieMonsterAuto/issues // @downloadURL https://raw.githubusercontent.com/fracergu/CookieMonsterAuto/main/CookieMonsterAuto.user.js // @updateURL https://raw.githubusercontent.com/fracergu/CookieMonsterAuto/main/CookieMonsterAuto.user.js // @grant GM_setValue // @grant GM_getValue // @grant unsafeWindow // @run-at document-idle // ==/UserScript== (() => { 'use strict'; const DEFAULTS = { autoClickGolden: true, autoClickWrath: false, autoBuyUpgrades: true, autoBuyBuildings: true, autoBuyMinColour: 0, autoBuyGray: false, autoClickCookie: false, autoClickCookieMs: 100, }; const COLOUR_TIERS = [ ['Blue', 'Green'], ['Blue', 'Green', 'Yellow'], ['Blue', 'Green', 'Yellow', 'Orange'], ['Blue', 'Green', 'Yellow', 'Orange', 'Red'], ['Blue', 'Green', 'Yellow', 'Orange', 'Red', 'Purple'], ]; const COLOUR_OPTION_LABELS = [ 'Blue + Green', 'Blue to Yellow', 'Blue to Orange', 'Blue to Red', 'Blue to Purple', ]; const get = (key) => { const v = GM_getValue(key, undefined); return v === undefined ? DEFAULTS[key] : v; }; const set = (key, value) => GM_setValue(key, value); const clampMs = (v) => { const n = parseInt(v, 10); if (Number.isNaN(n)) return 100; return Math.min(100000, Math.max(25, n)); }; const notify = (message) => { try { const Game = unsafeWindow.Game; if (Game && Game.Notify) Game.Notify('Cookie Monster Auto', message, '', 1, 1); else console.log('[Cookie Monster Auto]', message); } catch (e) { console.log('[Cookie Monster Auto]', message); } }; function tick() { const Game = unsafeWindow.Game; const data = unsafeWindow.CookieMonsterData; if (!Game || !Game.ready || !data || !data.Upgrades) return; if (Game.OnAscend) return; if (get('autoClickGolden')) { const clickWrath = get('autoClickWrath'); Object.keys(Game.shimmers).forEach((id) => { const shimmer = Game.shimmers[id]; if (shimmer && shimmer.type === 'golden' && (!shimmer.wrath || clickWrath)) { shimmer.pop(); } }); } const buyUpgrades = get('autoBuyUpgrades'); const buyBuildings = get('autoBuyBuildings'); if (!buyUpgrades && !buyBuildings) return; const allowed = COLOUR_TIERS[get('autoBuyMinColour')] || COLOUR_TIERS[0]; if (buyUpgrades) { const buyGray = get('autoBuyGray'); Object.keys(data.Upgrades).forEach((name) => { const cached = data.Upgrades[name]; if (!cached) return; if (!allowed.includes(cached.colour) && !(buyGray && cached.colour === 'Gray')) return; const up = Game.Upgrades[name]; if (up && up.unlocked && up.getPrice() <= Game.cookies) up.buy(); }); } if (buyBuildings) { const bulks = [ { amount: 1, cache: data.Objects1 }, { amount: 10, cache: data.Objects10 }, { amount: 100, cache: data.Objects100 }, ]; let bestIndex = -1; let bestAmount = 0; let bestPP = Infinity; Object.keys(Game.Objects).forEach((idx) => { if (Game.Objects[idx].locked) return; let best = null; bulks.forEach((b) => { const c = b.cache && b.cache[idx]; if (c && (!best || c.pp < best.cache.pp)) best = { cache: c, amount: b.amount }; }); if ( best && allowed.includes(best.cache.colour) && best.cache.price <= Game.cookies && best.cache.pp < bestPP ) { bestIndex = idx; bestAmount = best.amount; bestPP = best.cache.pp; } }); if (bestIndex !== -1) Game.Objects[bestIndex].buy(bestAmount); } } let lastCookieClick = 0; function cookieTick() { const Game = unsafeWindow.Game; if (!Game || !Game.ready || Game.OnAscend) return; if (!get('autoClickCookie')) return; const now = Date.now(); const ms = clampMs(get('autoClickCookieMs')); if (now - lastCookieClick >= ms) { lastCookieClick = now; Game.ClickCookie(null, 0); } } function injectMenuSection() { const Game = unsafeWindow.Game; if (!Game || Game.onMenu !== 'prefs') return; const menu = document.getElementById('menu'); if (!menu || !menu.childNodes[2]) return; const container = menu.childNodes[2]; const section = document.createElement('div'); section.className = 'subsection'; section.id = 'cookieMonsterAutoMenuSection'; const title = document.createElement('div'); title.className = 'title'; title.style.fontSize = '18px'; title.textContent = 'Cookie Monster Auto'; section.appendChild(title); const addToggle = (key, labelText) => { const row = document.createElement('div'); row.className = 'listing'; const option = document.createElement('a'); option.className = get(key) ? 'option' : 'option off'; option.textContent = get(key) ? 'ON' : 'OFF'; option.addEventListener('click', () => { set(key, !get(key)); Game.UpdateMenu(); }); const label = document.createElement('label'); label.style.lineHeight = '1.6'; label.textContent = labelText; row.appendChild(option); row.appendChild(label); section.appendChild(row); }; addToggle('autoClickGolden', 'Auto click golden cookies'); addToggle('autoClickWrath', 'Auto click wrath cookies'); addToggle('autoBuyUpgrades', 'Auto buy beneficial upgrades'); addToggle('autoBuyGray', 'Auto buy grey (utility) upgrades'); addToggle('autoBuyBuildings', 'Auto buy best buildings'); addToggle('autoClickCookie', 'Auto click the big cookie'); const msRow = document.createElement('div'); msRow.className = 'listing'; const msInput = document.createElement('input'); msInput.className = 'option'; msInput.type = 'number'; msInput.min = '25'; msInput.max = '100000'; msInput.step = '10'; msInput.value = String(clampMs(get('autoClickCookieMs'))); msInput.addEventListener('change', () => { set('autoClickCookieMs', clampMs(msInput.value)); Game.UpdateMenu(); }); const msLabel = document.createElement('label'); msLabel.style.lineHeight = '1.6'; msLabel.textContent = 'Big cookie click interval (ms)'; msRow.appendChild(msInput); msRow.appendChild(msLabel); section.appendChild(msRow); const row = document.createElement('div'); row.className = 'listing'; const colourOption = document.createElement('a'); colourOption.className = 'option'; colourOption.textContent = COLOUR_OPTION_LABELS[get('autoBuyMinColour') % COLOUR_OPTION_LABELS.length]; colourOption.addEventListener('click', () => { set('autoBuyMinColour', (get('autoBuyMinColour') + 1) % COLOUR_TIERS.length); Game.UpdateMenu(); }); const label = document.createElement('label'); label.style.lineHeight = '1.6'; label.textContent = 'Minimum quality to auto buy'; row.appendChild(colourOption); row.appendChild(label); section.appendChild(row); container.insertBefore(section, container.childNodes[container.childNodes.length - 1]); } let updateMenuWrapped = false; function start() { const Game = unsafeWindow.Game; if (!Game || updateMenuWrapped) return; // Game.UpdateMenu ya es el wrapper de Cookie Monster; encadenamos sin pisarlo. const originalUpdateMenu = Game.UpdateMenu; Game.UpdateMenu = function () { originalUpdateMenu(); injectMenuSection(); }; updateMenuWrapped = true; setInterval(tick, 200); setInterval(cookieTick, 25); notify('Cookie Monster Auto loaded'); } let warnedCM = false; const readyCheck = setInterval(() => { const Game = unsafeWindow.Game; if (!Game || !Game.ready) return; const cmLoaded = Game.mods && Game.mods.CookieMonster; const dataReady = unsafeWindow.CookieMonsterData && unsafeWindow.CookieMonsterData.Upgrades; if (cmLoaded && dataReady) { clearInterval(readyCheck); start(); } else if (!cmLoaded && !warnedCM) { warnedCM = true; console.warn('[Cookie Monster Auto] Cookie Monster not detected. Install and enable the "Cookie Monster" userscript first.'); } }, 500); })();