Cookie Clicker Ultimate Automation

Automated clicker, auto-buy, auto-harvest, garden manager, stock market trader, and HUD overlay.

当前为 2025-12-03 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Cookie Clicker Ultimate Automation
// @name:zh-TW   餅乾點點樂全自動掛機輔助 (Cookie Clicker)
// @name:zh-CN   饼干点点乐全自动挂机辅助 (Cookie Clicker)
// @namespace    http://tampermonkey.net/
// @version      7.1
// @description  Automated clicker, auto-buy, auto-harvest, garden manager, stock market trader, and HUD overlay.
// @description:zh-TW 全功能自動掛機腳本:自動點擊、智慧購買、自動收成糖塊、花園自動補種/視覺輔助、股市自動交易、閒置魔法。
// @description:zh-CN 全功能自动挂机脚本:自动点击、智慧购买、自动收成糖块、花园自动补种/视觉辅助、股市自动交易、闲置魔法。
// @author       You
// @match        https://wws.justnainai.com/*
// @match        https://orteil.dashnet.org/cookieclicker/*
// @icon         https://orteil.dashnet.org/cookieclicker/img/favicon.ico
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_openInTab
// @require      https://code.jquery.com/jquery-3.6.0.min.js
// @license      MIT
// @homepage     https://greasyfork.org/zh-CN/scripts/557692-cookie-clicker-ultimate-automation
// @homepageURL  https://greasyfork.org/zh-CN/scripts/557692-cookie-clicker-ultimate-automation
// @supportURL   https://greasyfork.org/zh-CN/scripts/557692-cookie-clicker-ultimate-automation/feedback
// ==/UserScript==

(function() {
    'use strict';

    // ═══════════════════════════════════════════════════════════════
    // 1. 核心變量 & 狀態記憶
    // ═══════════════════════════════════════════════════════════════
    // v6.x 基礎功能
    let isClickEnabled = GM_getValue('isClickEnabled', false);
    let isBuyEnabled = GM_getValue('isBuyEnabled', false);
    let isGoldenEnabled = GM_getValue('isGoldenEnabled', false);
    let isSpellEnabled = GM_getValue('isSpellEnabled', true);
    let isGardenEnabled = GM_getValue('isGardenEnabled', false);
    let isResearchEnabled = GM_getValue('isResearchEnabled', true);

    // v7.0 新增功能
    let isStockEnabled = GM_getValue('isStockEnabled', false);
    let isSEEnabled = GM_getValue('isSEEnabled', false);

    // v7.1 新增功能
    let isGardenOverlayEnabled = GM_getValue('isGardenOverlayEnabled', false);

    // 統計計數
    let clickCount = 0;
    let buyUpgradeCount = 0;
    let buyBuildingCount = 0;
    let goldenCookieCount = 0;

    // Interval IDs
    let intervalId = null;
    let buyIntervalId = null;
    let goldenCheckIntervalId = null;
    let gardenIntervalId = null;
    let stockIntervalId = null;
    let restartIntervalId = null;
    let countdownIntervalId = null;
    let promptCheckIntervalId = null;
    let buffCheckIntervalId = null;
    let ahkSignalIntervalId = null;
    let overlayIntervalId = null; // v7.1

    // 時間控制
    let nextRestartTime = 0;
    let nextBuyTime = 0;

    // 設定參數
    let gameVolume = GM_getValue('gameVolume', 50);
    let clickInterval = GM_getValue('clickInterval', 50);
    let buyStrategy = GM_getValue('buyStrategy', 'expensive');
    
    // 購買間隔
    let buyIntervalHours = GM_getValue('buyIntervalHours', 0);
    let buyIntervalMinutes = GM_getValue('buyIntervalMinutes', 3);
    let buyIntervalSeconds = GM_getValue('buyIntervalSeconds', 0);

    // 重啟間隔
    let restartIntervalHours = GM_getValue('restartIntervalHours', 1);
    let restartIntervalMinutes = GM_getValue('restartIntervalMinutes', 0);
    let restartIntervalSeconds = GM_getValue('restartIntervalSeconds', 0);

    const MAX_WIZARD_TOWERS = 800;
    
    // 花園陣型記憶
    let savedGardenPlot = GM_getValue('savedGardenPlot', Array(6).fill().map(() => Array(6).fill(-1)));

    // UI 位置
    let showCountdown = GM_getValue('showCountdown', true);
    let showBuffMonitor = GM_getValue('showBuffMonitor', true);
    let countdownX = GM_getValue('countdownX', window.innerWidth - 170);
    let countdownY = GM_getValue('countdownY', 10);
    let buffX = GM_getValue('buffX', window.innerWidth - 340);
    let buffY = GM_getValue('buffY', 150);
    let currentX = GM_getValue('buttonX', 50);
    let currentY = GM_getValue('buttonY', 50);

    // DOM 元素引用
    let countdownDisplay = null;
    let buffDisplay = null;
    let controlPanel = null;
    let floatingButton = null;

    const targetSelector = '#bigCookie';
    let originalTitle = document.title;

    // ═══════════════════════════════════════════════════════════════
    // 2. 初始化流程
    // ═══════════════════════════════════════════════════════════════
    function init() {
        console.log('🍪 Cookie Clicker Ultimate Automation v7.1 Loaded');

        const scriptRestarted = localStorage.getItem('cookieScriptRestarted');
        if (scriptRestarted) {
            console.log('🔄 Script restarted automatically.');
            localStorage.removeItem('cookieScriptRestarted');
        }

        if (currentX < 0 || currentX > window.innerWidth) currentX = 50;
        
        injectCustomStyles(); // v7.1 CSS
        createFloatingButton();
        createControlPanel();
        createCountdownDisplay();
        createBuffDisplay();
        setGameVolume(gameVolume);

        // 啟動各模組循環
        goldenCheckIntervalId = setInterval(checkGoldenCookie, 1000);
        promptCheckIntervalId = setInterval(autoConfirmPrompt, 1000);
        buffCheckIntervalId = setInterval(updateBuffDisplay, 500);
        ahkSignalIntervalId = setInterval(updateTitleForAHK, 1000);
        gardenIntervalId = setInterval(autoGarden, 2500);
        stockIntervalId = setInterval(autoStock, 3000);
        
        // v7.1 視覺輔助循環 (1秒一次)
        overlayIntervalId = setInterval(updateGardenOverlay, 1000);

        startCountdown();
        scheduleAutoRestart();

        setTimeout(() => {
            if (isClickEnabled) startAutoClick();
            if (isBuyEnabled) startAutoBuy();
            updateAllDisplays();
        }, 2000);

        document.addEventListener('keydown', function(e) {
            if (e.key === 'F8') {
                e.preventDefault();
                toggleAutoClick();
            }
        });
    }

    // v7.1 CSS 注入
    function injectCustomStyles() {
        const style = document.createElement('style');
        style.type = 'text/css';
        style.innerHTML = `
            /* 缺漏:藍色虛線 */
            .cc-overlay-missing {
                border: 3px dashed #2196f3 !important;
                box-sizing: border-box;
                background: rgba(33, 150, 243, 0.1);
            }
            /* 變異/異常:紅色發光 */
            .cc-overlay-anomaly {
                border: 3px solid #ff4444 !important;
                box-shadow: inset 0 0 15px rgba(255, 0, 0, 0.6) !important;
                box-sizing: border-box;
                z-index: 10;
            }
            /* 正確:綠色微弱 (可選) */
            .cc-overlay-correct {
                border: 1px solid rgba(76, 175, 80, 0.4) !important;
                box-sizing: border-box;
            }
        `;
        document.head.appendChild(style);
    }

    // ═══════════════════════════════════════════════════════════════
    // 3. AHK 通訊協議
    // ═══════════════════════════════════════════════════════════════
    function updateTitleForAHK() {
        if (typeof Game === 'undefined') return;

        let totalMult = 1;
        let isWorthClicking = false;

        if (Game.buffs) {
            for (let i in Game.buffs) {
                const buff = Game.buffs[i];
                if (buff.multCpS > 0) totalMult *= buff.multCpS;
                if (buff.multClick > 0) totalMult *= buff.multClick;

                if (buff.multClick > 1 || buff.multCpS > 7) {
                    isWorthClicking = true;
                }
            }
        }

        let coords = "0,0";
        const bigCookie = document.querySelector('#bigCookie');
        if (bigCookie) {
            const rect = bigCookie.getBoundingClientRect();
            const cx = Math.round(rect.left + rect.width / 2);
            const cy = Math.round(rect.top + rect.height / 2);
            coords = `${cx},${cy}`;
        }

        const signal = isWorthClicking ? "⚡ATTACK" : "💤IDLE";
        const displayMult = totalMult > 1000 ? (totalMult/1000).toFixed(1) + 'k' : Math.round(totalMult);
        document.title = `[${signal}|${displayMult}x|${coords}] ${originalTitle}`;
    }

    // ═══════════════════════════════════════════════════════════════
    // 4. 核心功能模組
    // ═══════════════════════════════════════════════════════════════

    // --- 點擊模組 ---
    function autoClick() {
        const element = document.querySelector(targetSelector);
        if (element) {
            element.click();
            clickCount++;
        }
    }

    function startAutoClick() {
        if (intervalId) clearInterval(intervalId);
        intervalId = setInterval(autoClick, clickInterval);
        isClickEnabled = true;
        updateAllDisplays();
    }

    function stopAutoClick() {
        if (intervalId) clearInterval(intervalId);
        isClickEnabled = false;
        updateAllDisplays();
    }

    function toggleAutoClick() {
        isClickEnabled = !isClickEnabled;
        GM_setValue('isClickEnabled', isClickEnabled);
        if (isClickEnabled) startAutoClick();
        else stopAutoClick();
    }

    // --- 花園模組 & 視覺輔助 (v7.1 Updated) ---
    function autoGarden() {
        if (!isGardenEnabled) return;
        if (typeof Game === 'undefined' || !Game.Objects['Farm'].minigameLoaded) return;
        
        const M = Game.Objects['Farm'].minigame;
        if (!M) return;

        for (let y = 0; y < 6; y++) {
            for (let x = 0; x < 6; x++) {
                if (!M.isTileUnlocked(x, y)) continue;

                const tile = M.plot[y][x];
                const tileId = tile[0];
                const tileAge = tile[1];
                
                if (tileId > 0) {
                    const plant = M.plantsById[tileId - 1];
                    if (plant.weed || plant.key === 'crumbspore') {
                        M.harvest(x, y);
                        continue;
                    }
                    if (tileAge >= 98 && tileAge >= plant.mature) {
                        M.harvest(x, y);
                        continue;
                    }
                }

                if (tileId === 0) {
                    const targetPlantId = savedGardenPlot[y][x];
                    if (targetPlantId !== -1 && targetPlantId !== null) {
                        const seed = M.plantsById[targetPlantId - 1];
                        if (seed && seed.unlocked && M.canPlant(seed)) {
                            M.useTool(seed.id, x, y);
                        }
                    }
                }
            }
        }
    }

    // v7.1 花園視覺覆蓋層
    function updateGardenOverlay() {
        if (!isGardenOverlayEnabled) return;
        if (typeof Game === 'undefined' || !Game.Objects['Farm'].minigameLoaded) return;
        
        const M = Game.Objects['Farm'].minigame;
        if (!M) return;

        // 檢查花園介面是否開啟
        const gardenPlot = document.getElementById('gardenPlot');
        if (!gardenPlot || gardenPlot.offsetParent === null) return;

        for (let y = 0; y < 6; y++) {
            for (let x = 0; x < 6; x++) {
                const tileDiv = document.getElementById(`gardenTile-${x}-${y}`);
                if (!tileDiv) continue;

                // 清除舊樣式
                tileDiv.classList.remove('cc-overlay-missing', 'cc-overlay-anomaly', 'cc-overlay-correct');

                // 略過未解鎖的格子
                if (!M.isTileUnlocked(x, y)) continue;

                const savedId = savedGardenPlot[y][x]; // -1 或 ID (ID從1開始)
                const realId = M.plot[y][x][0];        // 0 (空) 或 ID

                // 邏輯比對
                if (realId === 0 && savedId !== -1) {
                    // 1. 空位但記憶有植物 (Missing) -> 藍色虛線
                    tileDiv.classList.add('cc-overlay-missing');
                } else if (realId !== 0) {
                    // 2. 有植物
                    if (savedId !== -1 && realId !== savedId) {
                        // 異常:與記憶不符 (變異/雜草) -> 紅色發光
                        tileDiv.classList.add('cc-overlay-anomaly');
                    } else if (savedId === -1) {
                        // 異常:記憶是空的但長了東西 -> 紅色發光
                        tileDiv.classList.add('cc-overlay-anomaly');
                    } else if (realId === savedId) {
                        // 正確 -> 綠色微弱
                        tileDiv.classList.add('cc-overlay-correct');
                    }
                }
            }
        }
    }

    function saveGardenLayout() {
        if (typeof Game === 'undefined' || !Game.Objects['Farm'].minigame) {
            alert('花園尚未解鎖或載入!');
            return;
        }
        const M = Game.Objects['Farm'].minigame;
        let newLayout = [];
        for (let y = 0; y < 6; y++) {
            let row = [];
            for (let x = 0; x < 6; x++) {
                if (M.isTileUnlocked(x, y)) {
                    const tile = M.plot[y][x];
                    row.push(tile[0] === 0 ? -1 : tile[0]);
                } else {
                    row.push(-1);
                }
            }
            newLayout.push(row);
        }
        savedGardenPlot = newLayout;
        GM_setValue('savedGardenPlot', savedGardenPlot);
        
        const btn = $('#garden-save-btn');
        const originalText = btn.text();
        btn.text('✅ 已儲存!').css('background', '#4caf50');
        setTimeout(() => btn.text(originalText).css('background', '#2196f3'), 1500);
    }

    // --- 購買模組 ---
    function autoBuy() {
        if (typeof Game === 'undefined') return;

        if (Game.Upgrades['Elder Pledge']) {
            const pledge = Game.Upgrades['Elder Pledge'];
            if (pledge.unlocked && pledge.bought === 0 && pledge.canBuy()) {
                console.log('🛡️ [AutoBuy] 誓約過期,強制後台購買!');
                pledge.buy(); 
                nextBuyTime = Date.now() + getBuyInterval();
                return;
            }
        }

        if (isResearchEnabled) {
            const research = document.querySelectorAll('#techUpgrades .crate.upgrade.enabled');
            if (research.length > 0) {
                const item = research[0];
                console.log(`🛒 [購買-科技] 研究: ${getUpgradeName(item)}`);
                item.click();
                nextBuyTime = Date.now() + getBuyInterval();
                return;
            }
        }

        const upgradesList = document.querySelectorAll('#upgrades .crate.upgrade.enabled');
        if (upgradesList.length > 0) {
            const selected = selectByStrategy(upgradesList, true);
            if (selected) {
                console.log(`🛒 [購買-升級] 購買: ${getUpgradeName(selected)}`);
                selected.click();
                buyUpgradeCount++;
                nextBuyTime = Date.now() + getBuyInterval();
                return;
            }
        }

        const buildings = document.querySelectorAll('.product.unlocked.enabled');
        if (buildings.length > 0) {
            const selected = selectByStrategy(buildings, false);
            if (selected) {
                const buildId = selected.id.replace('product', '');
                let buildName = "未知建築";
                if(Game.ObjectsById[buildId]) buildName = Game.ObjectsById[buildId].displayName;

                console.log(`🛒 [購買-建築] 建造: ${buildName}`);
                selected.click();
                buyBuildingCount++;
                nextBuyTime = Date.now() + getBuyInterval();
            }
        }
    }

    function getUpgradeName(element) {
        let name = "未知物品";
        try {
            const id = element.getAttribute('data-id');
            if (id && Game.UpgradesById[id]) name = `${Game.UpgradesById[id].name}`;
        } catch(e) {}
        return name;
    }

    function selectByStrategy(items, isUpgrade) {
        if (!items || items.length === 0) return null;
        let itemsArray = Array.from(items);

        itemsArray = itemsArray.filter(item => {
            const id = item.getAttribute('data-id');
            if (isUpgrade && id === '84') return false; 
            
            if (!isUpgrade) {
                 const bId = item.id.replace('product', '');
                 if (Game.ObjectsById[bId] && Game.ObjectsById[bId].name === 'Wizard tower') {
                     if (Game.ObjectsById[bId].amount >= MAX_WIZARD_TOWERS) return false;
                 }
            }
            return true;
        });

        if (itemsArray.length === 0) return null;

        if (buyStrategy === 'random') return itemsArray[Math.floor(Math.random() * itemsArray.length)];
        if (buyStrategy === 'cheapest') return itemsArray[0];
        return itemsArray[itemsArray.length - 1];
    }

    function startAutoBuy() {
        if (buyIntervalId) clearInterval(buyIntervalId);
        const interval = getBuyInterval();
        buyIntervalId = setInterval(autoBuy, interval);
        nextBuyTime = Date.now() + interval;
        autoBuy();
    }

    function stopAutoBuy() {
        if (buyIntervalId) clearInterval(buyIntervalId);
        nextBuyTime = 0;
    }

    function getBuyInterval() {
        return (buyIntervalHours * 3600 + buyIntervalMinutes * 60 + buyIntervalSeconds) * 1000;
    }

    // --- 股市模組 ---
    function autoStock() {
        if (!isStockEnabled) return;
        if (typeof Game === 'undefined') return;
        
        const Bank = Game.Objects['Bank'];
        if (!Bank || !Bank.minigameLoaded || !Bank.minigame) return;
        const M = Bank.minigame;

        for (let i = 0; i < M.goodsById.length; i++) {
            const good = M.goodsById[i];
            const price = M.getGoodPrice(good);
            const rv = M.getRestingVal(good.id);
            
            if (price < rv * 0.5) {
                const maxStock = M.getGoodMaxStock(good);
                if (good.stock < maxStock && Game.cookies > price) {
                    M.buyGood(good.id, 10000);
                }
            }

            if (price > rv * 1.5) {
                if (good.stock > 0) {
                    M.sellGood(good.id, 10000);
                    console.log(`📉 [股市] 獲利賣出 ${good.name} @ $${price.toFixed(2)} (RV: ${rv.toFixed(2)})`);
                }
            }
        }
    }

    // --- 金餅乾/糖塊/魔法模組 ---
    function checkGoldenCookie() {
        if (isGoldenEnabled) {
            autoHarvestLump();
            const goldenCookies = document.querySelectorAll('#shimmers > div.shimmer');
            if (goldenCookies.length > 0) {
                goldenCookies.forEach(cookie => cookie.click());
                goldenCookieCount++;
            }
        }
        
        if (isSpellEnabled || isSEEnabled) {
            autoCastSpell();
        }
    }

    function autoHarvestLump() {
        if (typeof Game === 'undefined') return;
        if (Game.canLumps()) {
            if (Game.lumpCurrentType === 3) return;
            let age = Date.now() - Game.lumpT;
            if (age >= Game.lumpRipeAge) {
                Game.clickLump();
            }
        }
    }

    function autoCastSpell() {
        if (typeof Game === 'undefined' || !Game.Objects['Wizard tower'].minigame) return;
        const M = Game.Objects['Wizard tower'].minigame;
        const spellHoF = M.spells['hand of fate'];
        const spellSE = M.spells['spontaneous edifice'];

        if (isSpellEnabled && M.magic >= M.getSpellCost(spellHoF)) {
            let shouldCast = false;
            for (let i in Game.buffs) {
                const buff = Game.buffs[i];
                if (buff.multCpS > 7 || buff.multClick > 10) { shouldCast = true; break; }
                if (buff.multCpS === 7 && M.magic >= M.magicM * 0.95) { shouldCast = true; break; }
            }
            if (shouldCast) {
                M.castSpell(spellHoF);
                console.log('🧙‍♂️ [AutoSpell] 觸發連擊:命運之手');
                return;
            }
        }

        if (isSEEnabled && M.magic >= M.getSpellCost(spellSE)) {
            if (M.magic < M.magicM * 0.95) return;
            if (Object.keys(Game.buffs).length > 0) return;
            if (document.querySelectorAll('.shimmer').length > 0) return;
            console.log('🧙‍♂️ [AutoSpell] 閒置期:免費召喚了一座建築 (Spontaneous Edifice)');
            M.castSpell(spellSE);
        }
    }

    // --- 彈窗確認 ---
    function autoConfirmPrompt() {
        const promptBox = document.querySelector('#prompt');
        if (!promptBox) return;

        const isVisible = promptBox.style.display !== 'none' && promptBox.offsetHeight > 0;

        if (isVisible) {
            const promptContent = document.querySelector('#promptContent');
            const yesButton = document.querySelector('#promptOption0');

            if (promptContent && yesButton) {
                const txt = promptContent.textContent;
                const keywords = ['Warning', 'One Mind', 'revoke', '警告', '不好的结果'];
                if (keywords.some(k => txt.includes(k))) {
                    console.log('⚠️ [Safe] Auto-confirming prompt:', txt);
                    yesButton.click();
                }
            }
        }
    }

    // ═══════════════════════════════════════════════════════════════
    // 5. UI 顯示與管理
    // ═══════════════════════════════════════════════════════════════
    function createBuffDisplay() {
        if (buffDisplay) return;
        buffDisplay = $(`
            <div id="cookie-buff-monitor" style="
                position: fixed; left: ${buffX}px; top: ${buffY}px;
                padding: 10px; background: rgba(0,0,0,0.85);
                color: white; border-radius: 12px; font-family: 'Microsoft YaHei', sans-serif;
                z-index: 999996;
                box-shadow: 0 4px 20px rgba(0,0,0,0.6);
                display: ${showBuffMonitor ? 'block' : 'none'};
                cursor: move; user-select: none;
                width: 320px; box-sizing: border-box;
                border: 1px solid rgba(255,255,255,0.2); backdrop-filter: blur(4px);
            ">
                <div style="
                    font-weight: bold; margin-bottom: 10px;
                    border-bottom: 2px solid rgba(255,255,255,0.2);
                    padding-bottom: 8px; text-align: center;
                    color: #ffd700; font-size: 16px; letter-spacing: 1px;
                ">🔥 Buff 監控</div>
                <div id="buff-list-content" style="display: flex; flex-direction: column; gap: 8px;"></div>
            </div>
        `);
        makeDraggable(buffDisplay, 'buffX', 'buffY');
        $('body').append(buffDisplay);
    }

    function updateBuffDisplay() {
        if (!showBuffMonitor || !buffDisplay) return;
        if (typeof Game === 'undefined') return;

        const buffList = $('#buff-list-content');
        buffList.empty();

        let totalCpsMult = 1;
        let totalClickMult = 1; 
        let hasBuff = false;

        if (Game.buffs) {
            for (let i in Game.buffs) {
                hasBuff = true;
                const buff = Game.buffs[i];

                if (buff.multCpS > 0) totalCpsMult *= buff.multCpS;
                if (buff.multClick > 0) totalClickMult *= buff.multClick;

                const timeLeft = Math.ceil(buff.time / 30);
                const iconX = buff.icon[0] * 48;
                const iconY = buff.icon[1] * 48;
                const iconUrl = 'img/icons.png';
                const isPowerful = buff.multCpS > 50 || buff.multClick > 10;
                const textColor = isPowerful ? '#ff4444' : 'white';
                const bgColor = isPowerful ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.2)';

                let multDisplay = '';
                if (buff.multCpS > 0 && buff.multCpS !== 1) multDisplay = `產量 x${Math.round(buff.multCpS*10)/10}`;
                if (buff.multClick > 0 && buff.multClick !== 1) {
                    if(multDisplay) multDisplay += ', ';
                    multDisplay += `點擊 x${Math.round(buff.multClick)}`;
                }

                buffList.append(`
                    <div style="
                        display: flex; align-items: center;
                        padding: 6px; background: ${bgColor};
                        border-radius: 8px; border: 1px solid rgba(255,255,255,0.05);
                    ">
                        <div style="
                            width: 48px; height: 48px;
                            background: url(${iconUrl}) -${iconX}px -${iconY}px;
                            margin-right: 12px; border-radius: 4px;
                            box-shadow: 0 0 5px rgba(0,0,0,0.5); flex-shrink: 0;
                        "></div>
                        <div style="flex: 1; overflow: hidden;">
                            <div style="font-size: 15px; font-weight: bold; color: ${textColor}; margin-bottom: 2px;">
                                ${buff.dname ? buff.dname : buff.name}
                            </div>
                            <div style="font-size: 13px; color: #ffd700;">${multDisplay}</div>
                            <div style="font-size: 12px; color: #ccc;">剩餘: ${formatTime(timeLeft)}</div>
                        </div>
                    </div>
                `);
            }
        }

        if (!hasBuff) buffList.append('<div style="text-align: center; color: #666; font-size: 13px; padding: 10px;">無活性效果</div>');

        let summaryHtml = '<div style="margin-top: 8px; padding-top: 8px; border-top: 1px dashed rgba(255,255,255,0.3); text-align: right;">';
        let effectiveClickPower = totalCpsMult * totalClickMult;

        if (totalCpsMult !== 1) {
            let val = totalCpsMult < 1 ? totalCpsMult.toFixed(2) : Math.round(totalCpsMult).toLocaleString();
            summaryHtml += `<div style="color: #4caf50; font-weight: bold; font-size: 14px;">🏭 總產量: x${val}</div>`;
        }

        if (effectiveClickPower > 1) {
            let val = Math.round(effectiveClickPower).toLocaleString();
            summaryHtml += `<div style="color: #ff9800; font-weight: bold; font-size: 16px; margin-top: 2px;">⚡ 點擊倍率: x${val}</div>`;
        }

        if (typeof Game.computedMouseCps !== 'undefined') {
            let clickVal = Game.computedMouseCps;
            let formattedClick = typeof Beautify !== 'undefined' ? Beautify(clickVal) : Math.round(clickVal).toLocaleString();
            summaryHtml += `<div style="margin-top:5px; padding-top:5px; border-top:1px solid rgba(255,255,255,0.1); color:#fff; font-size:15px; font-family:monospace;">👆 點擊力: <span style="color: #ffd700;">+${formattedClick}</span></div>`;
        }
        summaryHtml += '</div>';
        buffList.append(summaryHtml);
    }

    function createCountdownDisplay() {
        if (countdownDisplay) return;
        countdownDisplay = $(`
            <div id="cookie-countdown" style="
                position: fixed; left: ${countdownX}px; top: ${countdownY}px;
                padding: 8px; background: rgba(0,0,0,0.85); color: white;
                border-radius: 8px; font-family: monospace; font-size: 12px;
                z-index: 999997; display: ${showCountdown ? 'block' : 'none'};
                width: 150px; cursor: move; border: 1px solid #444;
            ">
                <div style="text-align: center; border-bottom: 1px solid #555; margin-bottom: 4px;">⏱️ 倒數計時</div>
                <div style="display:flex; justify-content:space-between;"><span>🔄 重啟:</span><span id="txt-rst">--:--</span></div>
                <div style="display:flex; justify-content:space-between; margin-bottom:5px;"><span>🛒 購買:</span><span id="txt-buy">--:--</span></div>
                <div style="border-top:1px solid #555; padding-top:5px; text-align:center;">
                    <div style="font-size:10px; margin-bottom:2px;">🔊 音量: <span id="vol-disp">${gameVolume}</span>%</div>
                    <input type="range" id="volume-slider-mini" min="0" max="100" value="${gameVolume}" style="width:90%; cursor:pointer;">
                </div>
            </div>
        `);

        countdownDisplay.find('#volume-slider-mini').on('input', function() {
            gameVolume = parseInt($(this).val());
            $('#vol-disp').text(gameVolume);
            setGameVolume(gameVolume);
            GM_setValue('gameVolume', gameVolume);
        });

        makeDraggable(countdownDisplay, 'countdownX', 'countdownY');
        $('body').append(countdownDisplay);
    }

    function startCountdown() {
        if (countdownIntervalId) clearInterval(countdownIntervalId);
        countdownIntervalId = setInterval(() => {
            if (!showCountdown || !countdownDisplay) return;
            const now = Date.now();
            const rstRem = Math.max(0, nextRestartTime - now);
            const buyRem = Math.max(0, nextBuyTime - now);
            
            $('#txt-rst').text(formatMs(rstRem));
            $('#txt-buy').text(isBuyEnabled ? formatMs(buyRem) : '--:--');
            
            if (rstRem < 30000 && rstRem > 0) countdownDisplay.css('background', 'rgba(255,0,0,0.8)');
            else countdownDisplay.css('background', 'rgba(0,0,0,0.85)');
        }, 1000);
    }

    function createControlPanel() {
        if (controlPanel) return;
        const panelX = GM_getValue('panelX', window.innerWidth / 2 - 200);
        const panelY = GM_getValue('panelY', 100);

        controlPanel = $(`
            <div id="cookie-control-panel" style="
                position: fixed; left: ${panelX}px; top: ${panelY}px; width: 420px;
                max-height: 85vh; background: #fff; border-radius: 12px;
                box-shadow: 0 10px 40px rgba(0,0,0,0.4); z-index: 999998;
                font-family: Arial, sans-serif; display: none; overflow: hidden;
                color: #333;
            ">
                <div id="panel-header" style="
                    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
                    color: white; padding: 15px; font-weight: bold; font-size: 18px;
                    cursor: move; display: flex; justify-content: space-between; align-items: center;
                ">
                    <span>🍪 控制面板 v7.1</span>
                    <span style="font-size:12px; opacity:0.8;">Garden Overlay</span>
                </div>
                <div style="padding: 20px; overflow-y: auto; max-height: calc(85vh - 60px);">
                    
                    <div class="panel-section" style="margin-bottom:15px; padding:10px; background:#f5f7fa; border-radius:8px;">
                        <div style="font-weight:bold; color:#222; margin-bottom:10px; border-bottom:2px solid #ddd; padding-bottom:5px;">🎛️ 核心模組開關</div>
                        <div style="display:grid; grid-template-columns: 1fr 1fr; gap:10px; color:#333;">
                            <label><input type="checkbox" id="chk-auto-click" ${isClickEnabled?'checked':''}> 👉 自動點擊</label>
                            <label><input type="checkbox" id="chk-auto-buy" ${isBuyEnabled?'checked':''}> 🛒 自動購買</label>
                            <label><input type="checkbox" id="chk-auto-golden" ${isGoldenEnabled?'checked':''}> ⭐ 金餅乾/糖塊</label>
                            <label><input type="checkbox" id="chk-auto-garden" ${isGardenEnabled?'checked':''}> 🌻 花園維護</label>
                        </div>
                    </div>

                    <!-- v7.0 New Features -->
                    <div class="panel-section" style="margin-bottom:15px; padding:10px; background:#e1f5fe; border-radius:8px;">
                        <div style="font-weight:bold; color:#0277bd; margin-bottom:5px;">📈 進階掛機</div>
                        <div style="color:#333; font-size:13px; display:grid; gap:8px;">
                            <label><input type="checkbox" id="chk-stock" ${isStockEnabled?'checked':''}> 股市自動交易 (低買高賣, RV演算法)</label>
                            <label><input type="checkbox" id="chk-se" ${isSEEnabled?'checked':''}> 🧙‍♂️ 閒置魔法: 憑空建築 (免費獲取建築)</label>
                        </div>
                    </div>

                    <div class="panel-section" style="margin-bottom:15px; padding:10px; background:#e8f5e9; border-radius:8px;">
                        <div style="font-weight:bold; color:#1b5e20; margin-bottom:5px;">🌻 花園自動化設定</div>
                        <div style="font-size:12px; color:#333; margin-bottom:8px;">包含:除草、收割瀕死植物、自動補種</div>
                        
                        <label style="display:block; margin-bottom:8px; font-weight:bold; color:#2e7d32;">
                            <input type="checkbox" id="chk-garden-overlay" ${isGardenOverlayEnabled?'checked':''}> [x] 顯示陣型輔助網格 (Visual Aid)
                        </label>

                        <button id="garden-save-btn" style="
                            width:100%; padding:8px; background:#2196f3; color:white; border:none; border-radius:4px; cursor:pointer;
                        ">💾 記憶當前陣型 (用於自動補種)</button>
                        <div style="font-size:11px; color:#444; margin-top:5px; text-align:center;">
                            輔助網格:<span style="color:#2196f3">藍色虛線=缺漏</span>,<span style="color:#f44336">紅色發光=變異/異常</span>
                        </div>
                    </div>

                    <div class="panel-section" style="margin-bottom:15px; padding:10px; background:#fff3e0; border-radius:8px;">
                        <div style="font-weight:bold; color:#e65100; margin-bottom:5px;">🛒 購買策略 (含巫師塔上限)</div>
                        <select id="buy-strategy" style="width:100%; padding:5px; margin-bottom:5px; color:#333;">
                            <option value="expensive" ${buyStrategy==='expensive'?'selected':''}>最貴優先 (衝產量)</option>
                            <option value="cheapest" ${buyStrategy==='cheapest'?'selected':''}>最便宜優先</option>
                        </select>
                        <div style="display:flex; gap:5px; align-items:center; margin-top:5px; color:#333;">
                            <span style="font-size:13px;">間隔:</span>
                            <select id="buy-min" style="color:#333;">${generateOptions(0, 59, buyIntervalMinutes, '分')}</select>
                            <select id="buy-sec" style="color:#333;">${generateOptions(0, 59, buyIntervalSeconds, '秒')}</select>
                        </div>
                        <div style="font-size:11px; color:#b71c1c; margin-top:5px; line-height:1.4;">
                            ⚠️ 巫師塔上限: ${MAX_WIZARD_TOWERS}座 (保留魔力上限)<br>
                            ⚠️ 始終拒買: 長者盟約 (Elder Covenant)
                        </div>
                        <label style="display:block; margin-top:5px; font-size:12px; color:#333;">
                            <input type="checkbox" id="chk-research" ${isResearchEnabled?'checked':''}> 包含科技研發
                        </label>
                    </div>

                    <div class="panel-section" style="margin-bottom:10px; padding:10px; background:#f3e5f5; border-radius:8px;">
                        <div style="font-weight:bold; color:#4a148c; margin-bottom:5px;">⚙️ 其他設定</div>
                        <label style="display:block; font-size:13px; color:#333;"><input type="checkbox" id="chk-spell" ${isSpellEnabled?'checked':''}> 🧙‍♂️ 基礎魔法連擊 (命運之手)</label>
                        <label style="display:block; font-size:13px; color:#333;"><input type="checkbox" id="chk-ui-count" ${showCountdown?'checked':''}> ⏱️ 倒數計時</label>
                        <label style="display:block; font-size:13px; color:#333;"><input type="checkbox" id="chk-ui-buff" ${showBuffMonitor?'checked':''}> 🔥 Buff 監控</label>
                        
                        <div style="margin-top:12px; color:#333;">
                            <span style="font-size:13px; font-weight:bold;">點擊速度: <span id="spd-val">${clickInterval}</span>ms</span>
                            <input type="range" id="spd-slider" min="10" max="200" value="${clickInterval}" style="width:100%; margin-top: 8px;">
                        </div>
                        
                        <div style="margin-top:10px; border-top:1px solid #ccc; padding-top:8px; color:#333;">
                             <span style="font-size:13px;">自動重啟:</span>
                             <select id="rst-hr" style="color:#333;">${generateOptions(0, 24, restartIntervalHours, '時')}</select>
                             <select id="rst-min" style="color:#333;">${generateOptions(0, 59, restartIntervalMinutes, '分')}</select>
                             <button id="btn-force-restart" style="float:right; background:#ff5252; color:white; border:none; padding:4px 10px; border-radius:4px; cursor:pointer;">立即重啟</button>
                        </div>
                    </div>

                    <div style="text-align:center; color:#777; font-size:11px; margin-top:10px;">
                        Cookie Clicker Ultimate v7.1
                    </div>
                </div>
            </div>
        `);

        makeDraggable(controlPanel, 'panelX', 'panelY', '#panel-header');
        $('body').append(controlPanel);
        bindPanelEvents();
    }

    function formatTime(seconds) {
        if (seconds < 60) return seconds + "s";
        const m = Math.floor(seconds / 60);
        const s = seconds % 60;
        return `${m}m ${s}s`;
    }

    function formatMs(ms) {
        if (ms <= 0) return "00:00";
        const totalSec = Math.floor(ms / 1000);
        const h = Math.floor(totalSec / 3600);
        const m = Math.floor((totalSec % 3600) / 60);
        const s = totalSec % 60;
        if (h > 0) return `${h}:${pad(m)}:${pad(s)}`;
        return `${pad(m)}:${pad(s)}`;
    }
    function pad(n) { return n < 10 ? '0'+n : n; }

    function generateOptions(min, max, selected, unit) {
        let html = '';
        for (let i = min; i <= max; i++) html += `<option value="${i}" ${i === selected ? 'selected' : ''}>${i}${unit}</option>`;
        return html;
    }

    function bindPanelEvents() {
        $('#chk-auto-click').change(function() { 
            isClickEnabled = this.checked; 
            GM_setValue('isClickEnabled', isClickEnabled);
            isClickEnabled ? startAutoClick() : stopAutoClick();
        });
        $('#chk-auto-buy').change(function() {
            isBuyEnabled = this.checked;
            GM_setValue('isBuyEnabled', isBuyEnabled);
            isBuyEnabled ? startAutoBuy() : stopAutoBuy();
        });
        $('#chk-auto-golden').change(function() {
            isGoldenEnabled = this.checked;
            GM_setValue('isGoldenEnabled', isGoldenEnabled);
        });
        $('#chk-auto-garden').change(function() {
            isGardenEnabled = this.checked;
            GM_setValue('isGardenEnabled', isGardenEnabled);
        });

        // v7.0 新開關
        $('#chk-stock').change(function() {
            isStockEnabled = this.checked;
            GM_setValue('isStockEnabled', isStockEnabled);
        });
        $('#chk-se').change(function() {
            isSEEnabled = this.checked;
            GM_setValue('isSEEnabled', isSEEnabled);
        });

        // v7.1 新開關
        $('#chk-garden-overlay').change(function() {
            isGardenOverlayEnabled = this.checked;
            GM_setValue('isGardenOverlayEnabled', isGardenOverlayEnabled);
            if (!isGardenOverlayEnabled) {
                // 關閉時清除所有標記
                $('.cc-overlay-missing, .cc-overlay-anomaly, .cc-overlay-correct').removeClass('cc-overlay-missing cc-overlay-anomaly cc-overlay-correct');
            }
        });

        $('#garden-save-btn').click(saveGardenLayout);

        $('#buy-strategy').change(function() { buyStrategy = $(this).val(); GM_setValue('buyStrategy', buyStrategy); });
        $('#chk-research').change(function() { isResearchEnabled = this.checked; GM_setValue('isResearchEnabled', isResearchEnabled); });
        $('#chk-spell').change(function() { isSpellEnabled = this.checked; GM_setValue('isSpellEnabled', isSpellEnabled); });
        
        $('#chk-ui-count').change(function() { showCountdown = this.checked; GM_setValue('showCountdown', showCountdown); if(countdownDisplay) countdownDisplay.toggle(showCountdown); });
        $('#chk-ui-buff').change(function() { showBuffMonitor = this.checked; GM_setValue('showBuffMonitor', showBuffMonitor); if(buffDisplay) buffDisplay.toggle(showBuffMonitor); });

        $('#spd-slider').on('input', function() {
            clickInterval = parseInt($(this).val());
            $('#spd-val').text(clickInterval);
            GM_setValue('clickInterval', clickInterval);
            if (isClickEnabled) startAutoClick();
        });
        
        $('#buy-min, #buy-sec').change(function() {
            buyIntervalMinutes = parseInt($('#buy-min').val());
            buyIntervalSeconds = parseInt($('#buy-sec').val());
            GM_setValue('buyIntervalMinutes', buyIntervalMinutes);
            GM_setValue('buyIntervalSeconds', buyIntervalSeconds);
            if (isBuyEnabled) startAutoBuy();
        });

        $('#rst-hr').change(function() {
            restartIntervalHours = parseInt($(this).val());
            GM_setValue('restartIntervalHours', restartIntervalHours);
            scheduleAutoRestart();
        });

        $('#rst-min').change(function() {
            restartIntervalMinutes = parseInt($(this).val());
            GM_setValue('restartIntervalMinutes', restartIntervalMinutes);
            scheduleAutoRestart();
        });

        $('#btn-force-restart').click(function() {
            if(confirm('確定要刷新頁面嗎?')) performRestart();
        });
    }

    function makeDraggable(element, keyX, keyY, handleSelector = null) {
        let isDragging = false, startX = 0, startY = 0;
        const handle = handleSelector ? element.find(handleSelector) : element;
        
        handle.on('mousedown', function(e) {
            if ($(e.target).is('input, select, button')) return;
            isDragging = true;
            startX = e.clientX - parseInt(element.css('left'));
            startY = e.clientY - parseInt(element.css('top'));
            element.css('cursor', 'grabbing');
        });

        $(document).on('mousemove', function(e) {
            if (isDragging) {
                const x = e.clientX - startX;
                const y = e.clientY - startY;
                element.css({left: x + 'px', top: y + 'px'});
            }
        }).on('mouseup', function() {
            if (isDragging) {
                isDragging = false;
                element.css('cursor', 'default');
                GM_setValue(keyX, parseInt(element.css('left')));
                GM_setValue(keyY, parseInt(element.css('top')));
            }
        });
    }

    function createFloatingButton() {
        if (floatingButton) return;
        floatingButton = $(`
            <div id="cookie-floating-button" style="
                position: fixed; left: ${currentX}px; top: ${currentY}px;
                width: 50px; height: 50px;
                background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
                color: white; border-radius: 50%;
                display: flex; align-items: center; justify-content: center;
                cursor: pointer; z-index: 999999; font-size: 24px;
                box-shadow: 0 4px 15px rgba(0,0,0,0.3); user-select: none;
                transition: transform 0.1s;
            ">🍪</div>
        `);
        floatingButton.click((e) => { e.stopPropagation(); toggleControlPanel(); });
        
        let isD = false;
        floatingButton.mousedown(() => isD = true);
        $(document).mousemove((e) => {
            if(isD) {
                currentX = e.clientX - 25; currentY = e.clientY - 25;
                floatingButton.css({left: currentX, top: currentY});
            }
        }).mouseup(() => { 
            if(isD) { isD = false; GM_setValue('buttonX', currentX); GM_setValue('buttonY', currentY); }
        });
        
        $('body').append(floatingButton);
    }

    function toggleControlPanel() {
        if (!controlPanel) createControlPanel();
        controlPanel.is(':visible') ? controlPanel.fadeOut(200) : controlPanel.fadeIn(200);
    }

    function performRestart() {
        if (typeof Game !== 'undefined' && Game.WriteSave) Game.WriteSave();
        localStorage.setItem('cookieScriptRestarted', 'true');
        setTimeout(() => {
            GM_openInTab(window.location.href, { active: true, insert: true, setParent: false });
            setTimeout(() => window.close(), 1000);
        }, 500);
    }

    function scheduleAutoRestart() {
        if (restartIntervalId) clearInterval(restartIntervalId);
        let interval = (restartIntervalHours * 3600 + restartIntervalMinutes * 60 + restartIntervalSeconds) * 1000;
        if (interval < 60000) interval = 60000;
        nextRestartTime = Date.now() + interval;
        restartIntervalId = setInterval(performRestart, interval);
    }

    function setGameVolume(volume) {
        try { if (typeof Game !== 'undefined' && Game.setVolume) Game.setVolume(volume); } catch(e) {}
    }

    function updateAllDisplays() {
        if (floatingButton) {
            if (isClickEnabled) floatingButton.css('filter', 'hue-rotate(0deg)');
            else floatingButton.css('filter', 'grayscale(100%)');
        }
    }

    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', () => setTimeout(init, 1500));
    else setTimeout(init, 1500);

})();