// ==UserScript== // @name Cookie Clicker Ultimate Automation // @name:zh-TW 餅乾點點樂全自動掛機輔助 (Cookie Clicker) // @name:zh-CN 饼干点点乐全自动挂机辅助 (Cookie Clicker) // @namespace http://tampermonkey.net/ // @version 8.5.0 // @description Automated clicker, auto-buy, auto-harvest, garden manager, stock market, season manager, Santa evolver, and Smart Sugar Lump harvester. // @description:zh-TW 全功能自動掛機腳本 v8.5.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 // @downloadURL none // ==/UserScript== (function() { 'use strict'; // ═══════════════════════════════════════════════════════════════ // 0. 全域配置與狀態 (Configuration & State) // ═══════════════════════════════════════════════════════════════ const Config = { // 開關狀態 Flags: { // ✅ v8.5.0 新增:全局主開關 GlobalMasterSwitch: GM_getValue('isGlobalMasterSwitchEnabled', true), Click: GM_getValue('isClickEnabled', true), Buy: GM_getValue('isBuyEnabled', true), Golden: GM_getValue('isGoldenEnabled', true), // Controls both Golden Cookies and Sugar Lumps Spell: GM_getValue('isSpellEnabled', true), Garden: GM_getValue('isGardenEnabled', true), Research: GM_getValue('isResearchEnabled', true), AutoWrinkler: GM_getValue('isAutoWrinklerEnabled', true), Stock: GM_getValue('isStockEnabled', true), SE: GM_getValue('isSEEnabled', true), Season: GM_getValue('isSeasonEnabled', true), Santa: GM_getValue('isSantaEnabled', true), GardenOverlay: GM_getValue('isGardenOverlayEnabled', true), GardenAvoidBuff: GM_getValue('isGardenAvoidBuff', true), GardenMutation: GM_getValue('isGardenMutationEnabled', false), ShowCountdown: GM_getValue('showCountdown', true), ShowBuffMonitor: GM_getValue('showBuffMonitor', true), // v8.4.4 新增:花園保護開關 ShowGardenProtection: GM_getValue('showGardenProtection', true), SpendingLocked: GM_getValue('spendingLocked', false) }, // 參數 Settings: { Volume: GM_getValue('gameVolume', 50), ClickInterval: GM_getValue('clickInterval', 10), 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, SugarLumpGoal: 100, // Threshold for Bifurcated strategy SpellCooldownSuccess: 60000, // 施法成功冷卻時間 (毫秒) SpellCooldownFail: 60000, // v8.4.3 修改:從 5000 改為 60000 SEFailThreshold: 3, // v8.4.3 新增:失敗 3 次觸發轉換 SEFailResetCooldown: 300000 // v8.4.3 新增:轉換後 SE 冷卻 5 分鐘 }, // 記憶 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), // v8.4.4 新增:花園保護 UI 座標與狀態記憶 GardenProtectionX: GM_getValue('gardenProtectionX', 10), GardenProtectionY: GM_getValue('gardenProtectionY', 10), SavedSpendingStates: GM_getValue('savedSpendingStates', { Buy: true, Garden: true, Research: true, Stock: true }), // ✅ v8.5.0 新增:日誌面板座標記憶 ActionLogX: GM_getValue('actionLogX', window.innerWidth - 420), ActionLogY: GM_getValue('actionLogY', window.innerHeight - 350) } }; // 運行時計時器與緩存 const Runtime = { Timers: { NextBuy: 0, NextRestart: 0, NextGarden: 0, NextStock: 0, NextSeasonCheck: 0, NextSpontaneousEdifice: 0 // SE 法術冷卻計時器 }, Stats: { ClickCount: 0, BuyUpgradeCount: 0, BuyBuildingCount: 0, SEFailCount: 0 // v8.4.3 新增:SE 失敗計數器 }, OriginalTitle: document.title, SeasonState: { CurrentStage: 0, Roadmap: [ { name: 'Valentine', id: 'fools', target: 'BuyAllUpgrades' }, { name: 'Christmas', id: 'christmas', target: 'MaxSanta' } ] } }; // ═══════════════════════════════════════════════════════════════ // Logger 模組(✅ v8.5.0 新增) // 統一日誌封裝:同時輸出到 Console 與 UI.ActionLog // ═══════════════════════════════════════════════════════════════ const Logger = { /** * INFO 級別日誌(藍色) */ log: function(module, message) { const formatted = `[${module}] ${message}`; console.log(`ℹ️ ${formatted}`); if (UI.ActionLog && UI.ActionLog.append) { UI.ActionLog.append(formatted, 'info'); } }, /** * WARN 級別日誌(橙色) */ warn: function(module, message) { const formatted = `[${module}] ${message}`; console.warn(`⚠️ ${formatted}`); if (UI.ActionLog && UI.ActionLog.append) { UI.ActionLog.append(formatted, 'warn'); } }, /** * ERROR 級別日誌(紅色) */ error: function(module, message) { const formatted = `[${module}] ${message}`; console.error(`❌ ${formatted}`); if (UI.ActionLog && UI.ActionLog.append) { UI.ActionLog.append(formatted, 'error'); } }, /** * SUCCESS 級別日誌(綠色) */ success: function(module, message) { const formatted = `[${module}] ${message}`; console.log(`%c✅ ${formatted}`, 'color: #4caf50; font-weight: bold;'); if (UI.ActionLog && UI.ActionLog.append) { UI.ActionLog.append(formatted, 'success'); } } }; // ═══════════════════════════════════════════════════════════════ // 1. UI 與 日誌模組 // ═══════════════════════════════════════════════════════════════ 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); }, // ✅ v8.5.0 優化:支援 HH:MM:SS 格式 formatMs: function(ms) { if (ms < 0) return '00:00'; const totalSecs = Math.floor(ms / 1000); const h = Math.floor(totalSecs / 3600); const m = Math.floor((totalSecs % 3600) / 60); const s = totalSecs % 60; const pad = (n) => n < 10 ? '0' + n : n; if (h > 0) { return `${h}:${pad(m)}:${pad(s)}`; } return `${pad(m)}:${pad(s)}`; }, 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 = $(` `); 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+=``; return h; }; const buyMin = Math.floor(Config.Settings.BuyIntervalMs / 60000); const buySec = (Config.Settings.BuyIntervalMs % 60000) / 1000; const rstHr = Math.floor(Config.Settings.RestartIntervalMs / 3600000); const rstMin = (Config.Settings.RestartIntervalMs % 3600000) / 60000; this.Elements.Panel = $(` `); 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); }, // ✅ v8.5.0 新增:全局開關切換邏輯 toggleMasterSwitch: function() { Config.Flags.GlobalMasterSwitch = !Config.Flags.GlobalMasterSwitch; GM_setValue('isGlobalMasterSwitchEnabled', Config.Flags.GlobalMasterSwitch); const statusBar = $('#global-status-bar'); const statusIcon = $('#status-icon'); const statusText = $('#status-text'); const btn = $('#btn-toggle-master'); if (Config.Flags.GlobalMasterSwitch) { // 運行狀態 statusBar.css('background', '#4caf50'); statusIcon.text('🟢'); statusText.text('系統運行中'); btn.text('暫停 (F8)'); Logger.success('Core', '全局自動化已啟動'); } else { // 暫停狀態 statusBar.css('background', '#f44336'); statusIcon.text('🔴'); statusText.text('系統已暫停'); btn.text('恢復 (F8)'); Logger.warn('Core', '全局自動化已暫停'); } }, createCountdown: function() { if (this.Elements.Countdown) return; this.Elements.Countdown = $(` `); 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 = $(` `); 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(`
${buffName}
${multDisplay}
剩餘: ${Math.ceil(buff.time / 30)}s
`); } } if (!hasBuff) buffList.append('
無活性效果
'); let summaryHtml = '
'; let effectiveClickPower = totalCpsMult * totalClickMult; if (totalCpsMult !== 1) { let val = totalCpsMult < 1 ? totalCpsMult.toFixed(2) : Math.round(totalCpsMult).toLocaleString(); summaryHtml += `
🏭 總產量: x${val}
`; } if (effectiveClickPower > 1) { let val = Math.round(effectiveClickPower).toLocaleString(); summaryHtml += `
⚡ 點擊倍率: x${val}
`; } if (typeof Game.computedMouseCps !== 'undefined') { let clickVal = Game.computedMouseCps; let formattedClick = typeof Beautify !== 'undefined' ? Beautify(clickVal) : Math.round(clickVal).toLocaleString(); summaryHtml += `
👆 點擊力: +${formattedClick}
`; } summaryHtml += '
'; 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; // 新增:受保護功能的綁定(支援鎖定攔截) const bindChkWithLock = (id, key) => { $('#' + id).change(function() { if (Config.Flags.SpendingLocked) { this.checked = Config.Flags[key]; // 恢復原值 Logger.warn('花園保護', '支出鎖定期間無法修改此項目'); return false; } Config.Flags[key] = this.checked; GM_setValue('is' + key + 'Enabled', this.checked); if(key==='Click') self.updateButtonState(); }); }; const bindChk = (id, key) => { $('#'+id).change(function() { Config.Flags[key] = this.checked; GM_setValue('is'+key+(key.includes('Enabled')?'':'Enabled'), this.checked); 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'); bindChkWithLock('chk-auto-buy', 'Buy'); bindChk('chk-auto-golden', 'Golden'); bindChkWithLock('chk-auto-garden', 'Garden'); bindChkWithLock('chk-research', 'Research'); bindChk('chk-wrinkler', 'AutoWrinkler'); bindChkWithLock('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'); bindChk('chk-season', 'Season'); bindChk('chk-santa', 'Santa'); // ✅ v8.5.0 新增:日誌面板開關綁定 $('#chk-ui-log').change(function() { UI.ActionLog.toggle(this.checked); }); // ✅ v8.5.0 新增:全局開關綁定 $('#btn-toggle-master').click(function() { UI.toggleMasterSwitch(); }); $('#btn-toggle-master').hover( function() { $(this).css('background', 'rgba(255, 255, 255, 0.3)'); }, function() { $(this).css('background', 'rgba(255, 255, 255, 0.2)'); } ); // 新增花園保護開關綁定 $('#chk-show-garden-protection').change(function() { Config.Flags.ShowGardenProtection = this.checked; GM_setValue('showGardenProtection', this.checked); UI.GardenProtection.updateVisibility(); }); $('#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(); }); } }; // ═══════════════════════════════════════════════════════════════ // 可視化操作日誌面板(✅ v8.5.0 新增) // 遵循 UI 模組標準範本(基準守則第九章) // ═══════════════════════════════════════════════════════════════ UI.ActionLog = { Elements: { Container: null, LogList: null, Counter: null }, MaxEntries: 100, // 最大日誌條目數 create: function() { if (this.Elements.Container) return; this.Elements.Container = $(` `); $('body').append(this.Elements.Container); // 快取元素參考 this.Elements.LogList = $('#log-list'); this.Elements.Counter = $('#log-counter'); this.bindEvents(); // 使其可拖曳 UI.makeDraggable(this.Elements.Container, 'actionLogX', 'actionLogY'); console.log('✅ [ActionLog] 可視化日誌面板已創建'); }, bindEvents: function() { $('#btn-clear-log').click(() => this.clear()); $('#btn-close-log').click(() => this.toggle(false)); // Hover 效果 $('#btn-clear-log').hover( function() { $(this).css('background', '#d32f2f'); }, function() { $(this).css('background', '#f44336'); } ); $('#btn-close-log').hover( function() { $(this).css('background', '#616161'); }, function() { $(this).css('background', '#757575'); } ); }, append: function(message, level = 'info') { if (!this.Elements.LogList) return; // 級別顏色映射 const colors = { info: '#2196f3', warn: '#ff9800', error: '#f44336', success: '#4caf50' }; // 級別圖示映射 const icons = { info: 'ℹ️', warn: '⚠️', error: '❌', success: '✅' }; const timestamp = new Date().toLocaleTimeString('zh-TW', { hour12: false }); const color = colors[level] || colors.info; const icon = icons[level] || icons.info; const entry = $(`
${timestamp} ${icon} ${UI.cleanName(message)}
`); // 移除「尚無日誌」提示 this.Elements.LogList.find('div:contains("尚無日誌")').remove(); // 添加到列表頂部(最新日誌在上方) this.Elements.LogList.prepend(entry); // 限制最大條目數 const entries = this.Elements.LogList.children(); if (entries.length > this.MaxEntries) { entries.last().remove(); } // 更新計數器 this.Elements.Counter.text(`${entries.length} 條`); }, clear: function() { if (!this.Elements.LogList) return; this.Elements.LogList.html(`
尚無日誌
`); this.Elements.Counter.text('0 條'); console.log('🗑️ [ActionLog] 日誌已清空'); }, toggle: function(visible) { if (!this.Elements.Container) return; if (visible) { this.Elements.Container.fadeIn(200); $('#chk-ui-log').prop('checked', true); } else { this.Elements.Container.fadeOut(200); $('#chk-ui-log').prop('checked', false); } } }; // ═══════════════════════════════════════════════════════════════ // 花園保護模組(v8.4.4 新增,v8.4.7 擴充) // ═══════════════════════════════════════════════════════════════ UI.GardenProtection = { Elements: { Container: null }, SavedStates: { Buy: null, Garden: null, Research: null, Stock: null }, _cachedGardenPanel: null, create: function() { if (this.Elements.Container) return; const Farm = Game.Objects['Farm']; if (!Farm || !Farm.minigameLoaded) { Logger.warn('花園保護', '花園尚未解鎖,UI 創建已跳過'); return; } const gardenPanel = document.getElementById('gardenPanel'); if (!gardenPanel) return; this.Elements.Container = $(` `); $(gardenPanel).append(this.Elements.Container); // ✅ v8.4.6 修復:使用 jQuery 事件綁定(符合 CSP) $('#spending-lock-label').hover( function() { $(this).css('background', 'rgba(255, 255, 255, 0.2)'); }, function() { $(this).css('background', 'rgba(255, 255, 255, 0.1)'); } ); // ✅ v8.4.7 新增:按鈕 Hover 效果 $('#btn-save-garden-layout').hover( function() { $(this).css('background', '#66bb6a'); }, function() { $(this).css('background', '#81c784'); } ); this.bindEvents(); UI.makeDraggable(this.Elements.Container, 'gardenProtectionX', 'gardenProtectionY'); console.log('✅ [花園保護] UI 已創建'); }, bindEvents: function() { $('#chk-spending-lock').change(function() { UI.GardenProtection.toggle(this.checked); }); // ✅ v8.4.7 新增:記憶陣型按鈕綁定 $('#btn-save-garden-layout').click(function() { UI.GardenProtection.saveCurrentLayout(); }); }, toggle: function(enabled) { if (enabled) { // ===== 啟用停止支出 ===== this.SavedStates.Buy = Config.Flags.Buy; this.SavedStates.Garden = Config.Flags.Garden; this.SavedStates.Research = Config.Flags.Research; this.SavedStates.Stock = Config.Flags.Stock; Config.Flags.Buy = false; Config.Flags.Garden = false; Config.Flags.Research = false; Config.Flags.Stock = false; $('#chk-auto-buy').prop('checked', false).prop('disabled', true).css('opacity', '0.5'); $('#chk-auto-garden').prop('checked', false).prop('disabled', true).css('opacity', '0.5'); $('#chk-research').prop('checked', false).prop('disabled', true).css('opacity', '0.5'); $('#chk-stock').prop('checked', false).prop('disabled', true).css('opacity', '0.5'); this.showLockWarning(); Config.Memory.SavedSpendingStates = { ...this.SavedStates }; GM_setValue('savedSpendingStates', Config.Memory.SavedSpendingStates); GM_setValue('spendingLocked', true); Logger.log('花園保護', '已啟用支出鎖定'); } else { // ===== 解除停止支出 ===== Config.Flags.Buy = this.SavedStates.Buy !== null ? this.SavedStates.Buy : true; Config.Flags.Garden = this.SavedStates.Garden !== null ? this.SavedStates.Garden : true; Config.Flags.Research = this.SavedStates.Research !== null ? this.SavedStates.Research : true; Config.Flags.Stock = this.SavedStates.Stock !== null ? this.SavedStates.Stock : true; $('#chk-auto-buy').prop('checked', Config.Flags.Buy).prop('disabled', false).css('opacity', '1'); $('#chk-auto-garden').prop('checked', Config.Flags.Garden).prop('disabled', false).css('opacity', '1'); $('#chk-research').prop('checked', Config.Flags.Research).prop('disabled', false).css('opacity', '1'); $('#chk-stock').prop('checked', Config.Flags.Stock).prop('disabled', false).css('opacity', '1'); this.hideLockWarning(); this.SavedStates = { Buy: null, Garden: null, Research: null, Stock: null }; GM_setValue('spendingLocked', false); Logger.log('花園保護', '已解除支出鎖定,功能已恢復'); } Config.Flags.SpendingLocked = enabled; }, showLockWarning: function() { const panel = $('#cookie-control-panel'); if (panel.length && !$('#spending-lock-warning').length) { const warning = $(`
🔒 支出已鎖定 | 花園保護模式啟用中
`); panel.prepend(warning); } }, hideLockWarning: function() { $('#spending-lock-warning').remove(); }, updateVisibility: function() { if (!this.Elements.Container) return; if (!this._cachedGardenPanel) { this._cachedGardenPanel = document.getElementById('gardenPanel'); } if (!this._cachedGardenPanel) return; const isGardenOpen = this._cachedGardenPanel.style.display !== 'none'; if (Config.Flags.ShowGardenProtection && isGardenOpen) { this.Elements.Container.fadeIn(200); } else { this.Elements.Container.fadeOut(200); } }, // ✅ v8.4.7 新增:記憶當前陣型方法 saveCurrentLayout: function() { if (typeof Game === 'undefined' || !Game.Objects['Farm'].minigame) { Logger.warn('花園保護', '花園未就緒,無法記憶陣型'); if (typeof Game !== 'undefined' && Game.Notify) { Game.Notify('花園未就緒', '請先解鎖花園功能', [10, 6]); } return; } const M = Game.Objects['Farm'].minigame; let newLayout = []; let savedCount = 0; 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]; const tileId = tile[0]; // ✅ 核心邏輯:僅記憶已解鎖的種子 if (tileId === 0) { // 空格子:不記憶 row.push(-1); } else { const plant = M.plantsById[tileId - 1]; if (plant && plant.unlocked) { // 已解鎖種子:記憶 row.push(tileId); savedCount++; } else { // 未解鎖種子(紫框):不記憶 row.push(-1); } } } else { row.push(-1); } } newLayout.push(row); } // 儲存到配置 Config.Memory.SavedGardenPlot = newLayout; GM_setValue('savedGardenPlot', Config.Memory.SavedGardenPlot); // ✅ 反饋 1:Console 輸出 Logger.success('花園保護', `花園陣型已記憶(${savedCount} 種種子)`); // ✅ 反饋 2:遊戲通知 if (typeof Game !== 'undefined' && Game.Notify) { Game.Notify( '花園陣型已記憶', `已記錄 ${savedCount} 種種子`, [10, 6], 4 ); } // ✅ 反饋 3:按鈕視覺反饋 const btn = $('#btn-save-garden-layout'); const originalText = btn.text(); const originalBg = btn.css('background-color'); btn.text('✅ 已儲存!').css('background', '#4caf50'); setTimeout(() => { btn.text(originalText).css('background', originalBg); }, 1500); } }; // ═══════════════════════════════════════════════════════════════ // 2. 核心邏輯模組 (Business Logic) // ═══════════════════════════════════════════════════════════════ const Logic = { Click: { lastRun: 0, // v8.4.2 新增:魔力充足度檢查 (含浮點容差) isMagicSufficient: function(current, max, threshold = 0.95, tolerance = 0.001) { if (max === 0) return false; // 防止除以零 const ratio = current / max; return ratio >= (threshold - tolerance); }, update: function(now) { // ✅ v8.5.0:全局開關檢查 if (!Config.Flags.GlobalMasterSwitch) return; 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; } if (Config.Flags.Golden) { // v8.4.0: Sugar Lump Logic removed from here and moved to Logic.SugarLump document.querySelectorAll('#shimmers > div.shimmer').forEach(c => c.click()); } this.handleSpells(); }, // v8.4.3 核心修改:SE 施法邏輯(含失敗安全轉換) handleSpells: function() { if ((!Config.Flags.Spell && !Config.Flags.SE) || !Game.Objects['Wizard tower'].minigame) return; const M = Game.Objects['Wizard tower'].minigame; const now = Date.now(); // Hand of Fate 邏輯保持不變 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']); Logger.log('AutoSpell', '觸發連擊:命運之手'); } } // Spontaneous Edifice with Fail-Safe Conversion (v8.4.3) if (Config.Flags.SE && now >= Runtime.Timers.NextSpontaneousEdifice) { const spell = M.spells['spontaneous edifice']; const spellCost = M.getSpellCost(spell); if (M.magic >= spellCost && this.isMagicSufficient(M.magic, M.magicM, 0.95) && Object.keys(Game.buffs).length === 0 && document.querySelectorAll('.shimmer').length === 0) { const magicBefore = M.magic; const castResult = M.castSpell(spell); if (castResult && M.magic < magicBefore) { // ✅ 成功:重置計數器 Logger.log('AutoSpell', '閒置期:免費召喚了一座建築 (Spontaneous Edifice)'); Runtime.Stats.SEFailCount = 0; Runtime.Timers.NextSpontaneousEdifice = now + Config.Settings.SpellCooldownSuccess; } else { // ❌ 失敗:計數器 +1 Runtime.Stats.SEFailCount++; if (Runtime.Stats.SEFailCount >= Config.Settings.SEFailThreshold) { // 🔄 失敗達到閾值:轉換策略 const fthof = M.spells['hand of fate']; const fthofCost = M.getSpellCost(fthof); if (M.magic >= fthofCost) { const fthofBefore = M.magic; const fthofResult = M.castSpell(fthof); if (fthofResult && M.magic < fthofBefore) { Logger.log('AutoSpell', `SE 連續失敗 ${Config.Settings.SEFailThreshold} 次,已轉為施放 FtHoF`); Runtime.Stats.SEFailCount = 0; // 重置計數器 Runtime.Timers.NextSpontaneousEdifice = now + Config.Settings.SEFailResetCooldown; } else { Logger.warn('AutoSpell', 'FtHoF 施放失敗,SE 冷卻 5 分鐘'); Runtime.Stats.SEFailCount = 0; Runtime.Timers.NextSpontaneousEdifice = now + Config.Settings.SEFailResetCooldown; } } else { Logger.warn('AutoSpell', '魔力不足以施放 FtHoF,SE 冷卻 5 分鐘'); Runtime.Stats.SEFailCount = 0; Runtime.Timers.NextSpontaneousEdifice = now + Config.Settings.SEFailResetCooldown; } } else { // 失敗未達閾值:短冷卻 Logger.warn('AutoSpell', `SE 施法失敗 (${Runtime.Stats.SEFailCount}/${Config.Settings.SEFailThreshold}),冷卻 60 秒`); Runtime.Timers.NextSpontaneousEdifice = now + Config.Settings.SpellCooldownFail; } } } } }, 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))) { Logger.log('Safe', `Auto-confirming prompt: ${txt}`); yesButton.click(); } } } }, // v8.4.0 New Module SugarLump: { update: function(now) { // ✅ v8.5.0:全局開關檢查(雖然糖塊是高頻顯示,但點擊邏輯應受控) // 註:保留顯示更新,僅攔截收割動作 const statusEl = $('#lump-status'); if (!Config.Flags.Golden) { if (statusEl.length) statusEl.text('🍬 糖塊監控:已停用').css('color', '#999').css('border-left-color', '#999'); return; } if (typeof Game === 'undefined' || !Game.canLumps()) { if (statusEl.length) statusEl.text('🍬 糖塊監控:未解鎖').css('color', '#999').css('border-left-color', '#999'); return; } const age = Date.now() - Game.lumpT; const type = Game.lumpCurrentType; const ripeAge = Game.lumpRipeAge; let statusText = ''; let statusColor = '#666'; let borderColor = '#ccc'; let action = ''; // Decision Matrix switch (type) { case 3: // Meaty statusText = '⛔ [肉色糖塊] 啟動保護:等待自然掉落'; statusColor = '#d32f2f'; // Red warning borderColor = '#d32f2f'; action = 'SKIP'; break; case 2: // Golden case 4: // Caramelized if (age >= ripeAge) action = 'HARVEST_NOW'; else { statusText = `💎 [稀有糖塊] 等待成熟 (${UI.formatMs(ripeAge - age)})`; statusColor = '#e65100'; // Orange borderColor = '#ffd700'; // Gold } break; case 1: // Bifurcated if (age >= ripeAge) { if ((Game.lumps / Config.Settings.SugarLumpGoal) > 0.9) { action = 'HARVEST_NOW'; // Smart harvest } else { // Fallback: still harvest if ripe to ensure progress, // but logic allows for customization if needed. // For v8.4 baseline, we treat ripe as harvestable to avoid stalling. action = 'HARVEST_NOW'; } } else { statusText = `🌿 [雙倍糖塊] 等待成熟 (${UI.formatMs(ripeAge - age)})`; statusColor = '#2e7d32'; // Green borderColor = '#4caf50'; } break; default: // Normal if (age >= ripeAge) action = 'HARVEST_NOW'; else { statusText = `🍬 [普通糖塊] 等待成熟 (${UI.formatMs(ripeAge - age)})`; statusColor = '#555'; borderColor = '#ccc'; } break; } // Execute Action with Safety Net if (action === 'HARVEST_NOW') { // ✅ v8.5.0: 全局開關攔截收割 if (Config.Flags.GlobalMasterSwitch) { // Final Safety Net: Double Check Type if (Game.lumpCurrentType !== 3) { Game.clickLump(); Logger.log('SmartLump', `自動收割糖塊 (Type: ${type})`); statusText = '⚡ 正在收割...'; statusColor = '#4caf50'; borderColor = '#4caf50'; } else { Logger.warn('SmartLump', '攔截危險操作:試圖點擊肉色糖塊!'); } } } if (statusEl.length) { statusEl.text(statusText).css({ 'color': statusColor, 'border-left-color': borderColor }); } } }, Buy: { update: function(now) { // ✅ v8.5.0:全局開關檢查 if (!Config.Flags.GlobalMasterSwitch) return; if (!Config.Flags.Buy || now < Runtime.Timers.NextBuy) return; if (typeof Game === 'undefined') return; // ════════════════════════════════════════════ // 1. Elder Pledge(最高優先級) // ════════════════════════════════════════════ const pledge = Game.Upgrades['Elder Pledge']; if (pledge && pledge.unlocked && pledge.canBuy()) { if (typeof Game.pledgeT === 'undefined' || Game.pledgeT <= 0) { Logger.log('自動購買', '誓約過期,強制優先購買!'); pledge.buy(); Runtime.Timers.NextBuy = now + Config.Settings.BuyIntervalMs; return; } } // ════════════════════════════════════════════ // 2. Research(科技研發) // ════════════════════════════════════════════ if (Config.Flags.Research) { const research = document.querySelectorAll('#techUpgrades .crate.upgrade.enabled'); if (research.length > 0) { const item = research[0]; let resName = "未知科技"; const dataId = item.getAttribute('data-id'); if (dataId && Game.UpgradesById[dataId]) { resName = Game.UpgradesById[dataId].dname || Game.UpgradesById[dataId].name; } else { const onMouseOver = item.getAttribute('onmouseover'); if (onMouseOver) { const match = /
(.+?)<\/div>/.exec(onMouseOver); if (match) resName = match[1]; } } Logger.log('自動購買', `研發科技:${UI.cleanName(resName)}`); item.click(); Runtime.Timers.NextBuy = now + Config.Settings.BuyIntervalMs; return; } } // ════════════════════════════════════════════ // 3. Upgrades(升級) // ════════════════════════════════════════════ let affordable = Game.UpgradesInStore.filter(u => u.canBuy()); affordable = affordable.filter(u => { if (u.id === 84) return false; // Covenant if (u.id === 85) return false; // Revoke Covenant if (u.id === 74) return false; // Pledge handled above if (u.pool === 'toggle') { const seasonSwitchIds = [182, 183, 184, 185, 209]; if (!seasonSwitchIds.includes(u.id)) { return false; } } if (Config.Flags.Season) { const seasonSwitchIds = [182, 183, 184, 185, 209]; if (seasonSwitchIds.includes(u.id)) { return false; } } return true; }); if (affordable.length > 0) { if (Config.Settings.BuyStrategy === 'expensive') { affordable.sort((a, b) => b.getPrice() - a.getPrice()); } else { affordable.sort((a, b) => a.getPrice() - b.getPrice()); } const target = affordable[0]; let upName = target.dname || target.name; Logger.log('自動購買-升級', UI.cleanName(upName)); target.buy(); Runtime.Stats.BuyUpgradeCount++; Runtime.Timers.NextBuy = now + Config.Settings.BuyIntervalMs; return; } // ════════════════════════════════════════════ // 4. Seed First Protocol(v8.4.7 修復)✅ // ════════════════════════════════════════════ if (Config.Flags.Garden) { const Farm = Game.Objects['Farm']; if (Farm.minigameLoaded && Farm.minigame) { const M = Farm.minigame; let needsSeeds = false; // 掃描花園檢查種子需求(簡化版) outerLoop: for (let y = 0; y < 6; y++) { for (let x = 0; x < 6; x++) { if (M.isTileUnlocked(x, y)) { const savedId = Config.Memory.SavedGardenPlot[y][x]; const currentTile = M.plot[y][x]; // ✅ 核心邏輯:有空格子 + 記憶中有種子 = 需要保護資金 if (savedId > -1 && currentTile[0] === 0) { const seed = M.plantsById[savedId - 1]; if (seed && seed.unlocked) { needsSeeds = true; break outerLoop; // 提早退出 } } } } } // ✅ 修改:只要需要種子就阻擋建築購買 if (needsSeeds) { if (Math.random() < 0.1) { // 降低日誌頻率 Logger.log('Resource', '資金保護中:優先保留給花園種子'); } return; // 阻擋建築購買 } } } // ════════════════════════════════════════════ // 5. Buildings(建築) // ════════════════════════════════════════════ let affordableBuildings = []; for (let i in Game.ObjectsById) { const obj = Game.ObjectsById[i]; if (obj.locked) continue; if (obj.name === 'Wizard tower' && obj.amount >= Config.Settings.MaxWizardTowers) continue; if (obj.price <= Game.cookies) { affordableBuildings.push(obj); } } if (affordableBuildings.length > 0) { if (Config.Settings.BuyStrategy === 'expensive') { affordableBuildings.sort((a, b) => b.price - a.price); } else { affordableBuildings.sort((a, b) => a.price - b.price); } const targetB = affordableBuildings[0]; let buildName = targetB.displayName || targetB.name; const domElement = document.getElementById('productName' + targetB.id); if (domElement) { buildName = domElement.innerText; } Logger.log('自動購買-建築', UI.cleanName(buildName)); targetB.buy(1); Runtime.Stats.BuyBuildingCount++; Runtime.Timers.NextBuy = now + Config.Settings.BuyIntervalMs; } } }, Wrinkler: { hasLoggedShiny: false, update: function(now) { // ✅ v8.5.0:全局開關檢查 if (!Config.Flags.GlobalMasterSwitch) return; if (!Config.Flags.AutoWrinkler) return; if (typeof Game === 'undefined' || !Game.wrinklers) return; let currentShinyPresent = false; for (let i in Game.wrinklers) { let w = Game.wrinklers[i]; if (w.phase > 0 && w.close === 1) { if (w.type === 1) { currentShinyPresent = true; if (!this.hasLoggedShiny) { Logger.success('Wrinkler', '發現閃光皺紋蟲!已啟動保護機制!'); this.hasLoggedShiny = true; } } else { w.hp = 0; } } } if (!currentShinyPresent) { this.hasLoggedShiny = false; } } }, Garden: { update: function(now) { // ✅ v8.5.0:全局開關檢查 if (!Config.Flags.GlobalMasterSwitch) return; 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); Logger.log('花園', `鏟除雜物/已知變異 (紅框): ${plantName}`); } else { if (tileAge >= plant.mature) { M.harvest(x, y); Logger.success('花園', `成功收割新品種種子 (紫框): ${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: { // ✅ v8.4.7 修復:簡化邏輯(移除價格計算) checkSeedPriority: function() { if (!Config.Flags.Garden) return false; const Farm = Game.Objects['Farm']; if (!Farm.minigameLoaded || !Farm.minigame) return false; const M = Farm.minigame; // 掃描花園檢查種子需求(簡化版) for (let y = 0; y < 6; y++) { for (let x = 0; x < 6; x++) { if (M.isTileUnlocked(x, y)) { const savedId = Config.Memory.SavedGardenPlot[y][x]; const currentTile = M.plot[y][x]; // ✅ 核心邏輯:有空格子 + 記憶中有種子 = 需要保護資金 if (savedId > -1 && currentTile[0] === 0) { const seed = M.plantsById[savedId - 1]; if (seed && seed.unlocked) { // ✅ 提早退出:只要有一個格子需要種植就暫停股市 return true; } } } } } return false; }, update: function(now) { // ✅ v8.5.0:全局開關檢查 if (!Config.Flags.GlobalMasterSwitch) return; if (!Config.Flags.Stock || now < Runtime.Timers.NextStock) return; const Bank = Game.Objects['Bank']; if (!Bank || !Bank.minigameLoaded || !Bank.minigame) return; // ✅ v8.4.7 修復:前置檢查種子需求 if (this.checkSeedPriority()) { if (Math.random() < 0.05) { // 低頻日誌 Logger.log('Stock', '暫停交易:優先保留資金給花園種子'); } Runtime.Timers.NextStock = now + 5000; // 5 秒後再檢查 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); Logger.log('股市', `獲利賣出 ${goodName} @ $${price.toFixed(2)} (RV: ${rv.toFixed(2)})`); } } } Runtime.Timers.NextStock = now + 3000; } }, Season: { update: function(now) { // ✅ v8.5.0:全局開關檢查 if (!Config.Flags.GlobalMasterSwitch) return; if (!Config.Flags.Season || now < Runtime.Timers.NextSeasonCheck) return; const currentStage = Runtime.SeasonState.Roadmap[Runtime.SeasonState.CurrentStage]; if (!currentStage) return; const currentSeason = Game.season; const targetSeasonId = currentStage.id; if (currentSeason !== targetSeasonId) { const switcher = Object.values(Game.Upgrades).find(u => u.toggle && u.season === targetSeasonId); if (switcher) { if (!switcher.bought && switcher.canBuy()) { Logger.log('Season', `切換季節至: ${currentStage.name}`); switcher.buy(); } } Runtime.Timers.NextSeasonCheck = now + 2000; return; } 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(); Logger.log('Season', `購買季節餅乾: ${u.name}`); } } ); } } else if (currentStage.target === 'MaxSanta') { if (Game.santaLevel >= 14) { const remaining = Object.values(Game.Upgrades).filter(u => u.season === targetSeasonId && !u.bought && u.unlocked); if (remaining.length === 0) isComplete = true; } } if (isComplete) { Logger.log('Season', `階段完成: ${currentStage.name}`); Runtime.SeasonState.CurrentStage++; } Runtime.Timers.NextSeasonCheck = now + 5000; } }, Santa: { update: function(now) { // ✅ v8.5.0:全局開關檢查 if (!Config.Flags.GlobalMasterSwitch) return; if (!Config.Flags.Santa || Game.season !== 'christmas') return; if (Game.Has('A festive hat') && !Game.Has('Santa Claus')) { } if (Game.santaLevel < 14) { if (typeof Game.UpgradeSanta === 'function') { Game.UpgradeSanta(); } } } }, 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 = { // ✅ v8.5.0 新增:遊戲就緒檢查器 waitForGame: function(callback, maxRetries = 100) { // 雙重檢查:Game.ready + DOM 完全載入 if (typeof Game !== 'undefined' && Game.ready && document.readyState === 'complete') { callback(); } else if (maxRetries > 0) { setTimeout(() => { this.waitForGame(callback, maxRetries - 1); }, 100); } else { Logger.error('Core', '遊戲初始化超時(10秒),部分功能可能無法使用'); } }, init: function() { Logger.success('Core', 'Cookie Clicker Ultimate v8.5.0 (Master Switch + Visual Log) Loaded'); const scriptRestarted = localStorage.getItem('cookieScriptRestarted'); if (scriptRestarted) { Logger.log('Core', '腳本已自動重啟'); localStorage.removeItem('cookieScriptRestarted'); } // UI 初始化(不依賴 Game 物件) UI.initStyles(); UI.createFloatingButton(); UI.createControlPanel(); UI.createCountdown(); UI.createBuffMonitor(); // ✅ v8.5.0 新增:日誌面板初始化 UI.ActionLog.create(); // 音量設定 try { if (Game.setVolume) Game.setVolume(Config.Settings.Volume); } catch(e) { Logger.warn('Core', '音量設定失敗,遊戲可能未完全載入'); } this.scheduleRestart(); this.startHeartbeat(); // 花園保護 UI 初始化(延遲 2 秒) setTimeout(() => { UI.GardenProtection.create(); const restoreLockState = () => { if ($('#chk-auto-buy').length === 0) { Logger.warn('花園保護', '主 UI 尚未就緒,延遲恢復狀態'); setTimeout(restoreLockState, 200); return; } const savedStates = GM_getValue('savedSpendingStates', null); if (savedStates && Config.Flags.SpendingLocked) { UI.GardenProtection.SavedStates = savedStates; $('#chk-spending-lock').prop('checked', true); UI.GardenProtection.toggle(true); Logger.log('花園保護', '已恢復上次的鎖定狀態'); } }; restoreLockState(); }, 2000); // 清理與狀態更新(延遲 3 秒) setTimeout(() => { Logic.Garden.clearOverlay(); UI.updateButtonState(); // ✅ v8.5.0 新增:顯示日誌面板 UI.ActionLog.toggle(true); Logger.log('Core', '初始化完成,所有模組已就緒'); }, 3000); // F8 快捷鍵綁定 document.addEventListener('keydown', function(e) { if (e.key === 'F8') { e.preventDefault(); // ✅ v8.5.0:切換全局開關(而非單一點擊開關) UI.toggleMasterSwitch(); } }); }, startHeartbeat: function() { const self = this; const fastLoop = () => { const now = Date.now(); Logic.Click.update(now); const nextDelay = Config.Flags.Click ? Math.max(10, Config.Settings.ClickInterval) : 1000; setTimeout(fastLoop, nextDelay); }; fastLoop(); setInterval(() => { const now = Date.now(); Logic.Buy.update(now); Logic.Garden.update(now); Logic.Garden.updateOverlay(); Logic.SugarLump.update(now); // v8.4.0: Added Smart Sugar Lump logic Logic.Wrinkler.update(now); Logic.Stock.update(now); Logic.Season.update(now); Logic.Santa.update(now); Logic.updateTitle(); Logic.Click.handlePrompts(); UI.updateBuffDisplay(); // ===== 新增:花園保護 UI 顯示控制 ===== UI.GardenProtection.updateVisibility(); 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; 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); } }; // ✅ v8.5.0 修改:啟動邏輯重構 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { Core.waitForGame(() => Core.init()); }); } else { Core.waitForGame(() => Core.init()); } })();