Cookie Clicker Ultimate Automation v8.0 (Reborn)

Automated clicker, auto-buy, auto-harvest, garden manager, stock market, season manager, and Santa evolver.

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

您需要先安装一个扩展,例如 篡改猴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 v8.0 (Reborn)
// @name:zh-TW   餅乾點點樂全自動掛機輔助 v8.0 (架構重構版)
// @namespace    http://tampermonkey.net/
// @version      8.0
// @description  Automated clicker, auto-buy, auto-harvest, garden manager, stock market, season manager, and Santa evolver.
// @description:zh-TW 全功能自動掛機腳本 v8.0:新增季節自動切換、聖誕老人自動進化、全新模組化架構。
// @author       You & AI Architect
// @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
// ==/UserScript==

(function() {
    'use strict';

    // ═══════════════════════════════════════════════════════════════
    // 0. 全域配置與狀態 (Configuration & State)
    // ═══════════════════════════════════════════════════════════════
    const Config = {
        // 開關狀態
        Flags: {
            Click: GM_getValue('isClickEnabled', false),
            Buy: GM_getValue('isBuyEnabled', false),
            Golden: GM_getValue('isGoldenEnabled', false),
            Spell: GM_getValue('isSpellEnabled', true),
            Garden: GM_getValue('isGardenEnabled', false),
            Research: GM_getValue('isResearchEnabled', true),
            Stock: GM_getValue('isStockEnabled', false),
            SE: GM_getValue('isSEEnabled', false),
            Season: GM_getValue('isSeasonEnabled', true), // v8.0 New
            Santa: GM_getValue('isSantaEnabled', true),   // v8.0 New
            
            // 花園細項
            GardenOverlay: GM_getValue('isGardenOverlayEnabled', false),
            GardenAvoidBuff: GM_getValue('isGardenAvoidBuff', true),
            GardenMutation: GM_getValue('isGardenMutationEnabled', false),
            
            // UI
            ShowCountdown: GM_getValue('showCountdown', true),
            ShowBuffMonitor: GM_getValue('showBuffMonitor', true)
        },
        // 參數
        Settings: {
            Volume: GM_getValue('gameVolume', 50),
            ClickInterval: GM_getValue('clickInterval', 50),
            BuyStrategy: GM_getValue('buyStrategy', 'expensive'),
            BuyIntervalMs: (GM_getValue('buyIntervalHours', 0) * 3600 + GM_getValue('buyIntervalMinutes', 3) * 60 + GM_getValue('buyIntervalSeconds', 0)) * 1000,
            RestartIntervalMs: (GM_getValue('restartIntervalHours', 1) * 3600 + GM_getValue('restartIntervalMinutes', 0) * 60 + GM_getValue('restartIntervalSeconds', 0)) * 1000,
            MaxWizardTowers: 800
        },
        // 記憶
        Memory: {
            SavedGardenPlot: GM_getValue('savedGardenPlot', Array(6).fill().map(() => Array(6).fill(-1))),
            PanelX: GM_getValue('panelX', window.innerWidth / 2 - 200),
            PanelY: GM_getValue('panelY', 100),
            BuffX: GM_getValue('buffX', window.innerWidth - 340),
            BuffY: GM_getValue('buffY', 150),
            CountdownX: GM_getValue('countdownX', window.innerWidth - 170),
            CountdownY: GM_getValue('countdownY', 10),
            ButtonX: GM_getValue('buttonX', 50),
            ButtonY: GM_getValue('buttonY', 50)
        }
    };

    // 運行時計時器與緩存
    const Runtime = {
        Timers: {
            NextBuy: 0,
            NextRestart: 0,
            NextGarden: 0,
            NextStock: 0,
            NextSeasonCheck: 0
        },
        Stats: {
            ClickCount: 0,
            BuyUpgradeCount: 0,
            BuyBuildingCount: 0
        },
        OriginalTitle: document.title,
        SeasonState: {
            CurrentStage: 0,
            // 季節路線圖 (Season Roadmap)
            Roadmap: [
                { name: 'Valentine', id: 'fools', target: 'BuyAllUpgrades' }, // 情人節 (內部ID fools 即 valentines)
                { name: 'Christmas', id: 'christmas', target: 'MaxSanta' }    // 聖誕節 + 聖誕老人
            ]
        }
    };

    // ═══════════════════════════════════════════════════════════════
    // 1. UI 與 日誌模組 (UI & Logging)
    // ═══════════════════════════════════════════════════════════════
    const UI = {
        Elements: {
            Panel: null,
            FloatingBtn: null,
            Countdown: null,
            BuffMonitor: null
        },

        initStyles: function() {
            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-new { border: 3px solid #9c27b0 !important; box-shadow: inset 0 0 15px rgba(156, 39, 176, 0.8), 0 0 10px rgba(156, 39, 176, 0.5) !important; box-sizing: border-box; z-index: 12; }
                .cc-overlay-correct { border: 1px solid rgba(76, 175, 80, 0.4) !important; box-sizing: border-box; }
            `;
            document.head.appendChild(style);
        },

        // 格式化時間輔助
        formatMs: function(ms) {
            const s = Math.floor(ms/1000);
            return `${Math.floor(s/60)}:${s%60<10?'0':''}${s%60}`;
        },

        // 清洗名稱輔助
        cleanName: function(str) {
            if (!str) return '';
            if (typeof str !== 'string') return String(str);
            return str.replace(/<[^>]*>/g, '').trim();
        },

        // 創建懸浮按鈕
        createFloatingButton: function() {
            if (this.Elements.FloatingBtn) return;
            this.Elements.FloatingBtn = $(`
                <div id="cookie-floating-button" style="
                    position: fixed; left: ${Config.Memory.ButtonX}px; top: ${Config.Memory.ButtonY}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); transition: filter 0.3s;
                ">🍪</div>
            `);
            
            this.Elements.FloatingBtn.click((e) => { e.stopPropagation(); this.togglePanel(); });
            
            let isD = false;
            this.Elements.FloatingBtn.mousedown(() => isD = true);
            $(document).mousemove((e) => {
                if(isD) { 
                    Config.Memory.ButtonX = e.clientX - 25; 
                    Config.Memory.ButtonY = e.clientY - 25; 
                    this.Elements.FloatingBtn.css({left: Config.Memory.ButtonX, top: Config.Memory.ButtonY}); 
                }
            }).mouseup(() => { 
                if(isD) { 
                    isD = false; 
                    GM_setValue('buttonX', Config.Memory.ButtonX); 
                    GM_setValue('buttonY', Config.Memory.ButtonY); 
                }
            });
            $('body').append(this.Elements.FloatingBtn);
            this.updateButtonState();
        },

        updateButtonState: function() {
            if (this.Elements.FloatingBtn) {
                this.Elements.FloatingBtn.css('filter', Config.Flags.Click ? 'hue-rotate(0deg)' : 'grayscale(100%)');
            }
        },

        // 創建控制面板
        createControlPanel: function() {
            if (this.Elements.Panel) return;
            const generateOptions = (min, max, sel, unit) => { 
                let h=''; for(let i=min; i<=max; i++) h+=`<option value="${i}" ${i === sel?'selected':''}>${i}${unit}</option>`; return h; 
            };

            // 分解 Config.Settings.BuyIntervalMs 為 分/秒 供 UI 顯示
            const buyMin = Math.floor(Config.Settings.BuyIntervalMs / 60000);
            const buySec = (Config.Settings.BuyIntervalMs % 60000) / 1000;
            // 分解 RestartIntervalMs
            const rstHr = Math.floor(Config.Settings.RestartIntervalMs / 3600000);
            const rstMin = (Config.Settings.RestartIntervalMs % 3600000) / 60000;

            this.Elements.Panel = $(`
                <div id="cookie-control-panel" style="
                    position: fixed; left: ${Config.Memory.PanelX}px; top: ${Config.Memory.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>🍪 控制面板 v8.0 (Reborn)</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" ${Config.Flags.Click?'checked':''}> 👉 自動點擊</label>
                                <label><input type="checkbox" id="chk-auto-buy" ${Config.Flags.Buy?'checked':''}> 🛒 自動購買</label>
                                <label><input type="checkbox" id="chk-auto-golden" ${Config.Flags.Golden?'checked':''}> ⭐ 金餅乾/糖塊</label>
                                <label><input type="checkbox" id="chk-auto-garden" ${Config.Flags.Garden?'checked':''}> 🌻 花園維護</label>
                            </div>
                        </div>

                        <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" ${Config.Flags.Stock?'checked':''}> 股市自動交易</label>
                                <label><input type="checkbox" id="chk-se" ${Config.Flags.SE?'checked':''}> 🧙‍♂️ 閒置魔法: 憑空建築</label>
                                <label><input type="checkbox" id="chk-season" ${Config.Flags.Season?'checked':''}> 🍂 季節管理 (v8.0)</label>
                                <label><input type="checkbox" id="chk-santa" ${Config.Flags.Santa?'checked':''}> 🎅 聖誕老人進化 (v8.0)</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>
                            <label style="display:block; margin-bottom:8px; font-weight:bold; color:#2e7d32;">
                                <input type="checkbox" id="chk-garden-overlay" ${Config.Flags.GardenOverlay?'checked':''}> [x] 顯示陣型輔助網格
                            </label>
                            <label style="display:block; margin-bottom:8px; font-weight:bold; color:#d84315;">
                                <input type="checkbox" id="chk-garden-mutation" ${Config.Flags.GardenMutation?'checked':''}> 🧬 啟用自動突變管理
                            </label>
                            <label style="display:block; margin-bottom:8px; font-weight:bold; color:#1565c0;">
                                <input type="checkbox" id="chk-garden-smart" ${Config.Flags.GardenAvoidBuff?'checked':''}> 🧠 聰明補種 (Buff 期間暫停)
                            </label>
                            <button id="garden-save-btn" style="
                                width:100%; padding:8px; background:#2196f3; color:white; border:none; border-radius:4px; cursor:pointer;
                            ">💾 記憶當前陣型</button>
                        </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;">
                                <option value="expensive" ${Config.Settings.BuyStrategy==='expensive'?'selected':''}>最貴優先</option>
                                <option value="cheapest" ${Config.Settings.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">${generateOptions(0, 59, buyMin, '分')}</select>
                                <select id="buy-sec">${generateOptions(0, 59, buySec, '秒')}</select>
                            </div>
                        </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" ${Config.Flags.Spell?'checked':''}> 🧙‍♂️ 基礎魔法連擊</label>
                            <label style="display:block; font-size:13px; color:#333;"><input type="checkbox" id="chk-ui-count" ${Config.Flags.ShowCountdown?'checked':''}> ⏱️ 倒數計時</label>
                            <label style="display:block; font-size:13px; color:#333;"><input type="checkbox" id="chk-ui-buff" ${Config.Flags.ShowBuffMonitor?'checked':''}> 🔥 Buff 監控</label>
                            
                            <div style="margin-top:12px; color:#333;">
                                <span style="font-size:13px; font-weight:bold;">點擊速度: <span id="spd-val">${Config.Settings.ClickInterval}</span>ms</span>
                                <input type="range" id="spd-slider" min="10" max="200" value="${Config.Settings.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">${generateOptions(0, 24, rstHr, '時')}</select>
                                 <select id="rst-min">${generateOptions(0, 59, rstMin, '分')}</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>
                </div>
            `);
            this.makeDraggable(this.Elements.Panel, 'panelX', 'panelY', '#panel-header');
            $('body').append(this.Elements.Panel);
            this.bindEvents();
        },

        togglePanel: function() {
            if (!this.Elements.Panel) this.createControlPanel();
            this.Elements.Panel.is(':visible') ? this.Elements.Panel.fadeOut(200) : this.Elements.Panel.fadeIn(200);
        },

        createCountdown: function() {
            if (this.Elements.Countdown) return;
            this.Elements.Countdown = $(`
                <div id="cookie-countdown" style="
                    position: fixed; left: ${Config.Memory.CountdownX}px; top: ${Config.Memory.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: ${Config.Flags.ShowCountdown ? 'block' : 'none'};
                    width: 150px; cursor: move; border: 1px solid #444;
                ">
                    <div style="text-align: center; border-bottom: 1px solid #555; margin-bottom: 4px;">⏱️ 倒數計時 v8.0</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">${Config.Settings.Volume}</span>%</div>
                        <input type="range" id="volume-slider-mini" min="0" max="100" value="${Config.Settings.Volume}" style="width:90%;">
                    </div>
                </div>
            `);
            this.Elements.Countdown.find('#volume-slider-mini').on('input', function() {
                Config.Settings.Volume = parseInt($(this).val());
                $('#vol-disp').text(Config.Settings.Volume);
                try { if (Game.setVolume) Game.setVolume(Config.Settings.Volume); } catch(e) {}
                GM_setValue('gameVolume', Config.Settings.Volume);
            });
            this.makeDraggable(this.Elements.Countdown, 'countdownX', 'countdownY');
            $('body').append(this.Elements.Countdown);
        },

        createBuffMonitor: function() {
            if (this.Elements.BuffMonitor) return;
            this.Elements.BuffMonitor = $(`
                <div id="cookie-buff-monitor" style="
                    position: fixed; left: ${Config.Memory.BuffX}px; top: ${Config.Memory.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;
                    display: ${Config.Flags.ShowBuffMonitor ? 'block' : 'none'}; cursor: move; width: 320px;
                    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;">🔥 Buff 監控</div>
                    <div id="buff-list-content" style="display: flex; flex-direction: column; gap: 8px;"></div>
                </div>
            `);
            this.makeDraggable(this.Elements.BuffMonitor, 'buffX', 'buffY');
            $('body').append(this.Elements.BuffMonitor);
        },

        updateBuffDisplay: function() {
            if (!Config.Flags.ShowBuffMonitor || !this.Elements.BuffMonitor) 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 iconUrl = 'img/icons.png';
                    const iconX = buff.icon[0] * 48;
                    const iconY = buff.icon[1] * 48;

                    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)';

                    const buffName = UI.cleanName(buff.dname ? buff.dname : buff.name);

                    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; flex-shrink: 0; border-radius:4px; box-shadow: 0 0 5px rgba(0,0,0,0.5);"></div>
                            <div>
                                <div style="font-size: 14px; font-weight: bold; color: ${textColor};">${buffName}</div>
                                <div style="font-size: 12px; color: #ffd700;">${multDisplay}</div>
                                <div style="font-size: 12px; color: #ccc;">剩餘: ${Math.ceil(buff.time / 30)}s</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>`;
            }
            summaryHtml += '</div>';
            buffList.append(summaryHtml);
        },

        makeDraggable: function(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'));
            });
            $(document).on('mousemove', function(e) {
                if (isDragging) element.css({left: e.clientX - startX + 'px', top: e.clientY - startY + 'px'});
            }).on('mouseup', function() {
                if (isDragging) { isDragging = false; GM_setValue(keyX, parseInt(element.css('left'))); GM_setValue(keyY, parseInt(element.css('top'))); }
            });
        },

        bindEvents: function() {
            const self = this;
            // 綁定 Checkbox -> Config.Flags -> GM_setValue
            const bindChk = (id, key) => {
                $('#'+id).change(function() {
                    Config.Flags[key] = this.checked;
                    GM_setValue('is'+key+(key.includes('Enabled')?'':'Enabled'), this.checked); // 兼容舊命名
                    // Special handlers
                    if(key==='Click') self.updateButtonState();
                    if(key==='ShowCountdown') self.Elements.Countdown.toggle(this.checked);
                    if(key==='ShowBuffMonitor') self.Elements.BuffMonitor.toggle(this.checked);
                    if(key==='GardenOverlay') Logic.Garden.clearOverlay();
                });
            };

            bindChk('chk-auto-click', 'Click');
            bindChk('chk-auto-buy', 'Buy');
            bindChk('chk-auto-golden', 'Golden');
            bindChk('chk-auto-garden', 'Garden');
            bindChk('chk-stock', 'Stock');
            bindChk('chk-se', 'SE');
            bindChk('chk-spell', 'Spell');
            bindChk('chk-ui-count', 'ShowCountdown');
            bindChk('chk-ui-buff', 'ShowBuffMonitor');
            bindChk('chk-garden-overlay', 'GardenOverlay');
            bindChk('chk-garden-mutation', 'GardenMutation');
            bindChk('chk-garden-smart', 'GardenAvoidBuff');
            
            // New v8.0 bindings
            bindChk('chk-season', 'Season');
            bindChk('chk-santa', 'Santa');

            $('#garden-save-btn').click(() => Logic.Garden.saveLayout());

            $('#buy-strategy').change(function() { Config.Settings.BuyStrategy = $(this).val(); GM_setValue('buyStrategy', Config.Settings.BuyStrategy); });
            $('#spd-slider').on('input', function() { Config.Settings.ClickInterval = parseInt($(this).val()); $('#spd-val').text(Config.Settings.ClickInterval); GM_setValue('clickInterval', Config.Settings.ClickInterval); });
            
            const updateBuyInt = () => {
                const min = parseInt($('#buy-min').val()); const sec = parseInt($('#buy-sec').val());
                Config.Settings.BuyIntervalMs = (min * 60 + sec) * 1000;
                GM_setValue('buyIntervalMinutes', min); GM_setValue('buyIntervalSeconds', sec);
            };
            $('#buy-min, #buy-sec').change(updateBuyInt);

            const updateRstInt = () => {
                const hr = parseInt($('#rst-hr').val()); const min = parseInt($('#rst-min').val());
                Config.Settings.RestartIntervalMs = (hr * 3600 + min * 60) * 1000;
                Core.scheduleRestart();
                GM_setValue('restartIntervalHours', hr); GM_setValue('restartIntervalMinutes', min);
            };
            $('#rst-hr, #rst-min').change(updateRstInt);

            $('#btn-force-restart').click(() => { if(confirm('確定要刷新?')) Core.performRestart(); });
        }
    };

    // ═══════════════════════════════════════════════════════════════
    // 2. 核心邏輯模組 (Business Logic)
    // ═══════════════════════════════════════════════════════════════
    const Logic = {
        
        // 模組:點擊與黃金餅乾
        Click: {
            lastRun: 0,
            update: function(now) {
                // 1. 大餅乾點擊
                if (Config.Flags.Click && now - this.lastRun >= Config.Settings.ClickInterval) {
                    const bigCookie = document.querySelector('#bigCookie');
                    if (bigCookie) { bigCookie.click(); Runtime.Stats.ClickCount++; }
                    this.lastRun = now;
                }

                // 2. 黃金餅乾 / 馴鹿 / 糖塊 (高頻檢測)
                if (Config.Flags.Golden) {
                    if (Game.canLumps() && Game.lumpCurrentType !== 3 && (Date.now() - Game.lumpT) >= Game.lumpRipeAge) Game.clickLump();
                    // 點擊 Shimmers (金餅乾、馴鹿)
                    document.querySelectorAll('#shimmers > div.shimmer').forEach(c => c.click());
                }

                // 3. 魔法
                this.handleSpells();

                // 4. 彈窗確認
                this.handlePrompts();
            },
            handleSpells: function() {
                if ((!Config.Flags.Spell && !Config.Flags.SE) || !Game.Objects['Wizard tower'].minigame) return;
                const M = Game.Objects['Wizard tower'].minigame;

                // HoF
                if (Config.Flags.Spell && M.magic >= M.getSpellCost(M.spells['hand of fate'])) {
                    let shouldCast = false;
                    for (let i in Game.buffs) {
                        if (Game.buffs[i].multCpS > 7 || Game.buffs[i].multClick > 10) { shouldCast = true; break; }
                        if (Game.buffs[i].multCpS === 7 && M.magic >= M.magicM * 0.95) { shouldCast = true; break; }
                    }
                    if (shouldCast) {
                        M.castSpell(M.spells['hand of fate']);
                        console.log('🧙‍♂️ [AutoSpell] 觸發連擊:命運之手'); 
                    }
                }
                // SE
                if (Config.Flags.SE && M.magic >= M.getSpellCost(M.spells['spontaneous edifice'])) {
                    if (M.magic >= M.magicM * 0.95 && Object.keys(Game.buffs).length === 0 && document.querySelectorAll('.shimmer').length === 0) {
                        console.log('🧙‍♂️ [AutoSpell] 閒置期:免費召喚了一座建築 (Spontaneous Edifice)'); 
                        M.castSpell(M.spells['spontaneous edifice']);
                    }
                }
            },
            handlePrompts: function() {
                const yesButton = document.querySelector('#promptOption0');
                if (yesButton && document.querySelector('#promptContent')) {
                    const txt = document.querySelector('#promptContent').textContent;
                    if (['Warning', 'One Mind', 'revoke', '警告', '不好的结果'].some(k => txt.includes(k))) {
                        console.log('⚠️ [Safe] Auto-confirming prompt:', txt); 
                        yesButton.click();
                    }
                }
            }
        },

        // 模組:自動購買
        Buy: {
            update: function(now) {
                if (!Config.Flags.Buy || now < Runtime.Timers.NextBuy) return;

                // 1. 誓約
                if (Game.Upgrades['Elder Pledge'] && Game.Upgrades['Elder Pledge'].unlocked && Game.Upgrades['Elder Pledge'].bought === 0 && Game.Upgrades['Elder Pledge'].canBuy()) {
                    console.log('🛡️ [AutoBuy] 誓約過期,強制後台購買!');
                    Game.Upgrades['Elder Pledge'].buy(); 
                    Runtime.Timers.NextBuy = now + Config.Settings.BuyIntervalMs; 
                    return;
                }

                // 2. 科技
                if (Config.Flags.Research) {
                    const research = document.querySelectorAll('#techUpgrades .crate.upgrade.enabled');
                    if (research.length > 0) { 
                        const item = research[0];
                        console.log(`🛒 [購買-科技] 研究: ${this.getUpgradeName(item)}`);
                        item.click(); 
                        Runtime.Timers.NextBuy = now + Config.Settings.BuyIntervalMs; 
                        return; 
                    }
                }

                // 3. 升級 (含季節餅乾)
                const upgradesList = document.querySelectorAll('#upgrades .crate.upgrade.enabled');
                if (upgradesList.length > 0) {
                    const selected = this.selectByStrategy(upgradesList, true);
                    if (selected) { 
                        console.log(`🛒 [購買-升級] 購買: ${this.getUpgradeName(selected)}`);
                        selected.click(); 
                        Runtime.Stats.BuyUpgradeCount++; 
                        Runtime.Timers.NextBuy = now + Config.Settings.BuyIntervalMs; 
                        return; 
                    }
                }

                // 4. 建築
                const buildings = document.querySelectorAll('.product.unlocked.enabled');
                if (buildings.length > 0) {
                    const selected = this.selectByStrategy(buildings, false);
                    if (selected) { 
                        const buildId = selected.id.replace('product', '');
                        let buildName = "未知建築";
                        if(Game.ObjectsById[buildId]) {
                            const obj = Game.ObjectsById[buildId];
                            const domName = document.getElementById('productName' + obj.id);
                            buildName = domName ? domName.innerText : (obj.displayName || obj.name); 
                        }
                        console.log(`🛒 [購買-建築] 建造: ${buildName}`); 
                        selected.click(); 
                        Runtime.Stats.BuyBuildingCount++; 
                        Runtime.Timers.NextBuy = now + Config.Settings.BuyIntervalMs; 
                    }
                }
            },
            getUpgradeName: function(element) {
                let name = "未知物品";
                try {
                    const id = element.getAttribute('data-id');
                    if (id && Game.UpgradesById[id]) {
                        const obj = Game.UpgradesById[id];
                        name = UI.cleanName(obj.dname || obj.name);
                    }
                } catch(e) {}
                return name;
            },
            selectByStrategy: function(items, isUpgrade) {
                let itemsArray = Array.from(items).filter(item => {
                    const id = item.getAttribute('data-id');
                    if (isUpgrade) {
                        if (id === '84') return false; // Chocolate egg check
                        // v8.0: 如果季節系統啟用,不要在此亂買切換開關,交給 Season Manager
                        if (Config.Flags.Season && Game.UpgradesById[id] && Game.UpgradesById[id].toggle) return false;
                    } 
                    if (!isUpgrade) {
                         const bId = item.id.replace('product', '');
                         if (Game.ObjectsById[bId] && Game.ObjectsById[bId].name === 'Wizard tower' && Game.ObjectsById[bId].amount >= Config.Settings.MaxWizardTowers) return false;
                    }
                    return true;
                });
                if (itemsArray.length === 0) return null;
                if (Config.Settings.BuyStrategy === 'random') return itemsArray[Math.floor(Math.random() * itemsArray.length)];
                if (Config.Settings.BuyStrategy === 'cheapest') return itemsArray[0];
                return itemsArray[itemsArray.length - 1];
            }
        },

        // 模組:花園
        Garden: {
            update: function(now) {
                if (!Config.Flags.Garden || now < Runtime.Timers.NextGarden) return;
                if (typeof Game === 'undefined' || !Game.Objects['Farm'].minigameLoaded) return;
                
                const M = Game.Objects['Farm'].minigame;
                if (!M) return;

                let isCpsBuffActive = false;
                if (Config.Flags.GardenAvoidBuff) {
                    for (let i in Game.buffs) {
                        if (Game.buffs[i].multCpS > 1) { isCpsBuffActive = true; break; }
                    }
                }

                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];
                        const savedId = Config.Memory.SavedGardenPlot[y][x];

                        // 有植物
                        if (tileId > 0) {
                            const plant = M.plantsById[tileId - 1];
                            const isAnomaly = (savedId !== -1 && tileId !== savedId) || (savedId === -1);
                            const plantName = UI.cleanName(plant.name);

                            // 非異變:正常收割
                            if (!isAnomaly) {
                                if (tileAge >= 98 && tileAge >= plant.mature) M.harvest(x, y); 
                                continue; 
                            }

                            // 異變處理
                            if (Config.Flags.GardenMutation) {
                                if (plant.unlocked) {
                                    M.harvest(x, y);
                                    console.log(`🧹 [花園] 鏟除雜物/已知變異 (紅框): ${plantName}`);
                                } else {
                                    if (tileAge >= plant.mature) {
                                        M.harvest(x, y);
                                        console.log(`🎉 [花園] 成功收割新品種種子 (紫框): ${plantName}`);
                                    }
                                }
                            } else {
                                if (plant.weed) M.harvest(x, y);
                            }
                            continue;
                        }

                        // 空地:補種
                        if (tileId === 0) {
                            if (savedId !== -1 && savedId !== null) {
                                const seed = M.plantsById[savedId - 1];
                                if (seed && seed.unlocked && M.canPlant(seed)) {
                                    if (Config.Flags.GardenAvoidBuff && isCpsBuffActive) continue;
                                    M.useTool(seed.id, x, y);
                                }
                            }
                        }
                    }
                }
                Runtime.Timers.NextGarden = now + 2500;
            },
            updateOverlay: function() {
                if (!Config.Flags.GardenOverlay) 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++) {
                        const tileDiv = document.getElementById(`gardenTile-${x}-${y}`);
                        if (!tileDiv) continue;
                        tileDiv.classList.remove('cc-overlay-missing', 'cc-overlay-anomaly', 'cc-overlay-correct', 'cc-overlay-new');
                        if (!M.isTileUnlocked(x, y)) continue;

                        const savedId = Config.Memory.SavedGardenPlot[y][x];
                        const realId = M.plot[y][x][0];

                        if (realId === 0 && savedId !== -1) {
                            tileDiv.classList.add('cc-overlay-missing');
                        } else if (realId !== 0) {
                            const plant = M.plantsById[realId - 1];
                            const isAnomaly = (savedId !== -1 && realId !== savedId) || (savedId === -1);
                            if (isAnomaly) {
                                if (plant.unlocked) tileDiv.classList.add('cc-overlay-anomaly');
                                else tileDiv.classList.add('cc-overlay-new');
                            } else if (realId === savedId) {
                                tileDiv.classList.add('cc-overlay-correct');
                            }
                        }
                    }
                }
            },
            clearOverlay: function() {
                $('.cc-overlay-missing, .cc-overlay-anomaly, .cc-overlay-correct, .cc-overlay-new').removeClass('cc-overlay-missing cc-overlay-anomaly cc-overlay-correct cc-overlay-new');
            },
            saveLayout: function() {
                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);
                }
                Config.Memory.SavedGardenPlot = newLayout;
                GM_setValue('savedGardenPlot', Config.Memory.SavedGardenPlot);
                const btn = $('#garden-save-btn');
                const originalText = btn.text();
                btn.text('✅ 已儲存!').css('background', '#4caf50');
                setTimeout(() => btn.text(originalText).css('background', '#2196f3'), 1500);
            }
        },

        // 模組:股市
        Stock: {
            update: function(now) {
                if (!Config.Flags.Stock || now < Runtime.Timers.NextStock) 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);
                    const goodName = UI.cleanName(good.name);
                    
                    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(`📉 [股市] 獲利賣出 ${goodName} @ $${price.toFixed(2)} (RV: ${rv.toFixed(2)})`);
                        }
                    }
                }
                Runtime.Timers.NextStock = now + 3000;
            }
        },

        // 模組:季節經理 (v8.0 NEW)
        Season: {
            update: function(now) {
                if (!Config.Flags.Season || now < Runtime.Timers.NextSeasonCheck) return;
                
                const currentStage = Runtime.SeasonState.Roadmap[Runtime.SeasonState.CurrentStage];
                // 如果已經跑完所有 Roadmap,預設邏輯 (保持現狀 或 留在 Christmas)
                if (!currentStage) return;

                const currentSeason = Game.season;
                const targetSeasonId = currentStage.id;

                // 1. 確保在正確的季節
                if (currentSeason !== targetSeasonId) {
                    const switcherName = targetSeasonId === 'fools' ? 'Lovesick biscuit' : 'Festive biscuit'; // 簡化邏輯,需擴充完整名稱
                    // 尋找開關
                    const switcher = Object.values(Game.Upgrades).find(u => u.toggle && u.season === targetSeasonId);
                    if (switcher) {
                        if (!switcher.bought && switcher.canBuy()) {
                            console.log(`🍂 [Season] 切換季節至: ${currentStage.name}`);
                            switcher.buy();
                        } else if (!switcher.unlocked) {
                            // 還沒解鎖開關? 通常發生在剛轉生,需要等待或手動購買前置
                            // 這裡不做動作,等待解鎖
                        }
                    }
                    Runtime.Timers.NextSeasonCheck = now + 2000;
                    return;
                }

                // 2. 執行季節目標
                let isComplete = false;
                if (currentStage.target === 'BuyAllUpgrades') {
                    // 檢查商店是否還有該季節的升級
                    const remaining = Object.values(Game.Upgrades).filter(u => u.season === targetSeasonId && !u.bought && u.unlocked);
                    if (remaining.length === 0) isComplete = true;
                    else {
                        // 嘗試購買
                        remaining.forEach(u => { if (u.canBuy()) { u.buy(); console.log(`🍂 [Season] 購買季節餅乾: ${u.name}`); } });
                    }
                } else if (currentStage.target === 'MaxSanta') {
                    // 聖誕老人邏輯委派給 Logic.Santa,這裡只檢查結果
                    if (Game.santaLevel >= 14) { // 14 or 15
                        // 也要買完餅乾
                        const remaining = Object.values(Game.Upgrades).filter(u => u.season === targetSeasonId && !u.bought && u.unlocked);
                        if (remaining.length === 0) isComplete = true;
                    }
                }

                // 3. 切換下一階段
                if (isComplete) {
                    console.log(`🍂 [Season] 階段完成: ${currentStage.name}`);
                    Runtime.SeasonState.CurrentStage++;
                }

                Runtime.Timers.NextSeasonCheck = now + 5000;
            }
        },

        // 模組:聖誕老人 (v8.0 NEW)
        Santa: {
            update: function(now) {
                if (!Config.Flags.Santa || Game.season !== 'christmas') return;
                
                // 解鎖聖誕老人
                if (Game.Has('A festive hat') && !Game.Has('Santa Claus')) {
                    // 需要購買帽子嗎?通常由 Buy 模組處理,這裡假設已擁有
                }

                // 進化邏輯
                // 檢查是否有進化按鈕可用
                if (Game.santaLevel < 14) { // 0-14 級
                    const price = Math.pow(Game.santaLevel + 1, Game.santaLevel + 1); // 簡化公式,實際上遊戲有內部定義 Game.santaDrops
                    // 實際上最穩的方法是模擬點擊
                    // 由於聖誕老人的 UI 是特殊的,我們直接調用內部函數或查找 DOM
                    
                    // DOM 方式: 需要開啟 Santa 面板
                    // 為求穩定,這裡使用內部函數 Game.UpgradeSanta() 如果可行的話,
                    // 但通常需要點擊 #santa 元素來打開面板,然後點擊 #santaUpgrade
                    
                    const santaEl = document.getElementById('santa');
                    if (santaEl && santaEl.style.display !== 'none') {
                        // 嘗試升級
                        // 這裡使用比較 hack 的方式:模擬使用者操作
                        // 由於這部分邏輯較複雜且依賴 UI 狀態,我們採用保守策略:
                        // 如果有足夠餅乾,且有 .santa-upgrade 按鈕 (假設面板已開),點它
                        // 如果面板沒開,點 santa
                        
                        // 簡單版:只在錢很多時嘗試
                        // Game.UpgradeSanta() 是內部函數
                        if (typeof Game.UpgradeSanta === 'function') {
                            Game.UpgradeSanta(); // 嘗試進化
                            // 這會扣錢並升級,如果錢不夠它自己會擋
                        }
                    }
                }
            }
        },

        // AHK 標題更新
        updateTitle: function() {
            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();
                coords = `${Math.round(rect.left + rect.width / 2)},${Math.round(rect.top + rect.height / 2)}`;
            }
            const signal = isWorthClicking ? "⚡ATTACK" : "💤IDLE";
            const displayMult = totalMult > 1000 ? (totalMult/1000).toFixed(1) + 'k' : Math.round(totalMult);
            document.title = `[${signal}|${displayMult}x|${coords}] ${Runtime.OriginalTitle}`;
        }
    };

    // ═══════════════════════════════════════════════════════════════
    // 3. 系統核心 (System Core)
    // ═══════════════════════════════════════════════════════════════
    const Core = {
        init: function() {
            console.log('🍪 Cookie Clicker Ultimate v8.0 (Reborn) Loaded');
            
            const scriptRestarted = localStorage.getItem('cookieScriptRestarted');
            if (scriptRestarted) {
                console.log('🔄 Script restarted automatically.');
                localStorage.removeItem('cookieScriptRestarted');
            }

            UI.initStyles();
            UI.createFloatingButton();
            UI.createControlPanel();
            UI.createCountdown();
            UI.createBuffMonitor();
            
            try { if (Game.setVolume) Game.setVolume(Config.Settings.Volume); } catch(e) {}

            this.scheduleRestart();
            this.startHeartbeat();
            
            // 延遲啟動自動化,等待遊戲加載
            setTimeout(() => {
                Logic.Garden.clearOverlay();
                UI.updateButtonState();
            }, 3000);

            // 快捷鍵
            document.addEventListener('keydown', function(e) {
                if (e.key === 'F8') {
                    e.preventDefault();
                    Config.Flags.Click = !Config.Flags.Click;
                    GM_setValue('isClickEnabled', Config.Flags.Click);
                    UI.updateButtonState();
                    // 更新 Checkbox
                    if(UI.Elements.Panel) $('#chk-auto-click').prop('checked', Config.Flags.Click);
                }
            });
        },

        // v8.0 新核心:單一循環
        startHeartbeat: function() {
            const self = this;
            
            // 1. Fast Tick (UI, Click, Title) - 每 50ms (或依設定)
            // 使用 setTimeout 遞迴以允許動態調整速度
            const fastLoop = () => {
                const now = Date.now();
                Logic.Click.update(now);
                setTimeout(fastLoop, Math.max(20, Config.Settings.ClickInterval)); // 限制最快 20ms
            };
            fastLoop();

            // 2. Slow Tick (Logic, Buy, Garden, Season) - 每 1000ms
            setInterval(() => {
                const now = Date.now();
                Logic.Buy.update(now);
                Logic.Garden.update(now);
                Logic.Garden.updateOverlay();
                Logic.Stock.update(now);
                Logic.Season.update(now);
                Logic.Santa.update(now);
                Logic.updateTitle();
                UI.updateBuffDisplay();
                
                // Countdown text update
                if (Config.Flags.ShowCountdown) {
                    $('#txt-rst').text(UI.formatMs(Math.max(0, Runtime.Timers.NextRestart - now)));
                    $('#txt-buy').text(Config.Flags.Buy ? UI.formatMs(Math.max(0, Runtime.Timers.NextBuy - now)) : '--:--');
                }
            }, 1000);
        },

        scheduleRestart: function() {
            if (Runtime.Timers.RestartInterval) clearInterval(Runtime.Timers.RestartInterval);
            let interval = Config.Settings.RestartIntervalMs;
            if (interval < 60000) interval = 60000;
            Runtime.Timers.NextRestart = Date.now() + interval;
            
            // 使用 setTimeout 做一次性觸發
            if(this.restartTimer) clearTimeout(this.restartTimer);
            this.restartTimer = setTimeout(() => this.performRestart(), interval);
        },

        performRestart: function() {
            if (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);
        }
    };

    // 啟動腳本
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', () => setTimeout(() => Core.init(), 1500));
    } else {
        setTimeout(() => Core.init(), 1500);
    }

})();