// ==UserScript== // @name Cookie Clicker Ultimate Automation // @name:zh-TW 餅乾點點樂全自動掛機輔助 (Cookie Clicker) // @name:zh-CN 饼干点点乐全自动挂机辅助 (Cookie Clicker) // @namespace http://tampermonkey.net/ // @version 8.5.5-reborn // @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.5 Reborn:右側嵌入式控制台 + 動態花園儀表板 // @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: { GlobalMasterSwitch: GM_getValue('isGlobalMasterSwitchEnabled', true), Click: GM_getValue('isClickEnabled', true), Buy: GM_getValue('isBuyEnabled', true), Golden: GM_getValue('isGoldenEnabled', true), 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), ShowGardenProtection: GM_getValue('showGardenProtection', true), SpendingLocked: GM_getValue('spendingLocked', false), // ⭐ 核心狀態 GodzamokCombo: GM_getValue('isGodzamokComboEnabled', false), ShowGardenGrid: GM_getValue('isShowGardenGrid', 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, SpellCooldownSuccess: 60000, SpellCooldownFail: 60000, SEFailThreshold: 3, SEFailResetCooldown: 300000, GodzamokMinMult: 1000, GodzamokSellAmount: 100, GodzamokTargetBuilding: 'Farm', GodzamokCooldown: 15000, GodzamokBuyBackTime: 11000 }, // 記憶 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), GardenProtectionX: GM_getValue('gardenProtectionX', 10), GardenProtectionY: GM_getValue('gardenProtectionY', 10), ActionLogX: GM_getValue('actionLogX', window.innerWidth - 420), ActionLogY: GM_getValue('actionLogY', window.innerHeight - 350), GardenGridX: GM_getValue('gardenGridX', 100), GardenGridY: GM_getValue('gardenGridY', 100), // ⭐ v8.5.3 新增 UI 記憶 LastActiveTab: GM_getValue('lastActiveTab', 'core'), GardenLeftExpanded: GM_getValue('gardenLeftExpanded', false), GardenRightExpanded: GM_getValue('gardenRightExpanded', false), LogFontSize: GM_getValue('logFontSize', 12), LogOpacity: GM_getValue('logOpacity', 0.95), SavedSpendingStates: GM_getValue('savedSpendingStates', { Buy: true, Garden: true, Research: true, Stock: true }), // ⭐ v8.5.5-reborn 修改:移除盾牌相關記憶,保留最小化狀態 GardenProtectionMinimized: GM_getValue('gardenProtectionMinimized', false) } }; // 運行時計時器與緩存 const Runtime = { Timers: { NextBuy: 0, NextRestart: 0, NextGarden: 0, NextStock: 0, NextSeasonCheck: 0, NextSpontaneousEdifice: 0, NextGodzamokCombo: 0 }, Stats: { ClickCount: 0, BuyUpgradeCount: 0, BuyBuildingCount: 0, SEFailCount: 0 }, GodzamokState: { isActive: false, soldAmount: 0, originalBuyState: true }, OriginalTitle: document.title, SeasonState: { CurrentStage: 0, Roadmap: [ { name: 'Valentine', id: 'fools', target: 'BuyAllUpgrades' }, { name: 'Christmas', id: 'christmas', target: 'MaxSanta' } ] } }; // ═══════════════════════════════════════════════════════════════ // Logger 模組 // ═══════════════════════════════════════════════════════════════ const Logger = { log: function(module, message) { const formatted = `[${module}] ${message}`; console.log(`ℹ️ ${formatted}`); if (UI.ActionLog && UI.ActionLog.append) UI.ActionLog.append(formatted, 'info'); }, warn: function(module, message) { const formatted = `[${module}] ${message}`; console.warn(`⚠️ ${formatted}`); if (UI.ActionLog && UI.ActionLog.append) UI.ActionLog.append(formatted, 'warn'); }, error: function(module, message) { const formatted = `[${module}] ${message}`; console.error(`❌ ${formatted}`); if (UI.ActionLog && UI.ActionLog.append) UI.ActionLog.append(formatted, 'error'); }, 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; } .cc-close-btn { position:absolute; top:5px; right:5px; cursor:pointer; color:#aaa; font-weight:bold; padding:2px 6px; z-index:100; font-family:sans-serif; } .cc-close-btn:hover { color:white; background:rgba(255,255,255,0.2); border-radius:4px; } /* Tab System - v8.5.4 響應式修復 */ .cc-tab-header { display: flex; border-bottom: 2px solid #ddd; background: #f5f7fa; white-space: nowrap; overflow-x: auto; overflow-y: hidden; } .cc-tab-btn { flex: 1 0 auto; padding: 12px 5px; border: none; background: transparent; cursor: pointer; font-weight: bold; color: #555; transition: all 0.2s; font-size: 14px; min-width: 0; overflow: hidden; text-overflow: ellipsis; } .cc-tab-btn:hover { background: rgba(0,0,0,0.05); color: #333; } .cc-tab-btn.active { border-bottom: 3px solid #667eea; color: #667eea; background: #fff; } .cc-tab-pane { display: none; padding: 15px; animation: fadeIn 0.2s; } .cc-tab-pane.active { display: block; } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } /* Log Controls */ .cc-log-controls { display: flex; gap: 10px; padding: 5px 10px; background: rgba(0,0,0,0.3); border-bottom: 1px solid #444; font-size: 12px; align-items: center; } .cc-range-mini { width: 60px; height: 4px; } /* Garden Grid V8.5.3 Drawer Styles - ✅ v8.5.4-hotfix 修正 */ .cc-garden-row { display: flex; align-items: flex-start; justify-content: center; } .cc-drawer { width: 0px; opacity: 0; overflow: hidden; transition: width 0.3s ease, opacity 0.2s ease; background: rgba(0,0,0,0.3); border-radius: 6px; } .cc-drawer.open { width: 220px; opacity: 1; margin: 0 5px; border: 1px solid #555; } .cc-center-stage { width: 320px !important; /* ✅ 固定寬度,防止抖動 */ flex-shrink: 0; display: flex; flex-direction: column; align-items: center; z-index: 10; } .cc-garden-side ul { list-style: none; padding: 0; margin: 0; } .cc-garden-side ul li { padding: 5px 8px; border-bottom: 1px solid #333; transition: background 0.2s; font-size: 13px; } .cc-garden-side ul li:hover { background: rgba(255, 255, 255, 0.1); } /* v8.5.5-reborn 新增:嵌入式控制台樣式 */ #cc-embed-right { position: absolute; top: 50%; right: 2px; transform: translateY(-50%); display: flex; flex-direction: column; gap: 6px; z-index: 1000; pointer-events: auto; } .cc-embed-btn { width: 32px; height: 32px; background: rgba(0, 0, 0, 0.8); border: 2px solid #81c784; border-radius: 50%; color: white; font-size: 14px; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s; box-shadow: 0 2px 5px rgba(0,0,0,0.5); } .cc-embed-btn:hover { background: rgba(129, 199, 132, 0.9); transform: scale(1.1); box-shadow: 0 0 10px rgba(129, 199, 132, 0.8); } .cc-embed-btn:active { transform: scale(0.95); } /* v8.5.5-reborn 新增:Rich Tooltip 樣式 */ #cc-tooltip { position: absolute; background: rgba(0, 0, 0, 0.9); color: white; padding: 8px 12px; border-radius: 6px; border: 1px solid #81c784; font-family: Arial, sans-serif; font-size: 12px; z-index: 10001; pointer-events: none; box-shadow: 0 4px 12px rgba(0,0,0,0.5); max-width: 200px; line-height: 1.4; } #cc-tooltip .tooltip-name { font-weight: bold; color: #81c784; margin-bottom: 4px; } #cc-tooltip .tooltip-price { display: flex; justify-content: space-between; } .price-affordable { color: #4caf50; font-weight: bold; } .price-unaffordable { color: #f44336; font-weight: bold; } `; document.head.appendChild(style); }, 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; return h > 0 ? `${h}:${pad(m)}:${pad(s)}` : `${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) { if (Config.Flags.GlobalMasterSwitch) { this.Elements.FloatingBtn.css({ 'filter': Config.Flags.Click ? 'hue-rotate(0deg)' : 'grayscale(100%)', 'background': 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)' }).html('🍪'); } else { this.Elements.FloatingBtn.css({ 'filter': 'none', 'background': '#f44336' }).html('⏸️'); } } }, 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(); this.restoreTab(); }, restoreTab: function() { const lastTab = Config.Memory.LastActiveTab; $(`.cc-tab-btn[data-target="${lastTab}"]`).click(); }, togglePanel: function() { if (!this.Elements.Panel) this.createControlPanel(); this.Elements.Panel.is(':visible') ? this.Elements.Panel.fadeOut(200) : this.Elements.Panel.fadeIn(200); }, 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', '全局自動化已暫停'); } this.updateButtonState(); }, 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.Elements.Countdown.find('#countdown-close').click(() => { $('#chk-ui-count').prop('checked', false).trigger('change'); }); this.makeDraggable(this.Elements.Countdown, 'countdownX', 'countdownY'); $('body').append(this.Elements.Countdown); }, createBuffMonitor: function() { if (this.Elements.BuffMonitor) return; this.Elements.BuffMonitor = $(` `); this.Elements.BuffMonitor.find('#buff-monitor-close').click(() => { $('#chk-ui-buff').prop('checked', false).trigger('change'); }); 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; $('.cc-tab-btn').click(function() { const target = $(this).data('target'); $('.cc-tab-btn').removeClass('active'); $(this).addClass('active'); $('.cc-tab-pane').removeClass('active'); $('#tab-' + target).addClass('active'); Config.Memory.LastActiveTab = target; GM_setValue('lastActiveTab', target); }); 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'); $('#chk-ui-log').change(function() { UI.ActionLog.toggle(this.checked); }); $('#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()); $('#main-panel-close').click(() => self.togglePanel()); $('#chk-godzamok').change(function() { Config.Flags.GodzamokCombo = this.checked; GM_setValue('isGodzamokComboEnabled', this.checked); }); $('#val-godzamok-amount').change(function() { Config.Settings.GodzamokSellAmount = parseInt(this.value); }); $('#val-godzamok-target').change(function() { Config.Settings.GodzamokTargetBuilding = this.value; }); $('#val-godzamok-min').change(function() { Config.Settings.GodzamokMinMult = parseInt(this.value); }); $('#btn-show-grid').click(() => UI.GardenGrid.toggle()); $('#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(); }); } }; // ═══════════════════════════════════════════════════════════════ // 可視化操作日誌面板 // ═══════════════════════════════════════════════════════════════ UI.ActionLog = { Elements: { Container: null, LogList: null, Counter: null }, MaxEntries: 100, lastMsgContent: null, lastMsgCount: 1, lastMsgElement: null, create: function() { if (this.Elements.Container) return; const fontSize = Config.Memory.LogFontSize; const opacity = Config.Memory.LogOpacity; 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'); }, bindEvents: function() { $('#btn-clear-log').click(() => this.clear()); $('#btn-clear-log').hover( function() { $(this).css('background', '#d32f2f'); }, function() { $(this).css('background', '#f44336'); } ); this.Elements.Container.find('#action-log-close').click(() => { this.toggle(false); }); $('#log-font-slider').on('input', function() { const size = $(this).val(); $('#action-log-panel').css('font-size', size + 'px'); Config.Memory.LogFontSize = size; GM_setValue('logFontSize', size); }); $('#log-opacity-slider').on('input', function() { const opacity = $(this).val() / 100; $('#action-log-panel').css('background', `rgba(0, 0, 0, ${opacity})`); Config.Memory.LogOpacity = opacity; GM_setValue('logOpacity', opacity); }); }, 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 color = colors[level] || colors.info; const icon = icons[level] || icons.info; const cleanMsg = UI.cleanName(message); if (this.lastMsgContent === cleanMsg && this.lastMsgElement && this.Elements.LogList.has(this.lastMsgElement).length) { this.lastMsgCount++; const timestamp = new Date().toLocaleTimeString('zh-TW', { hour12: false }); this.lastMsgElement.find('.log-time').text(timestamp); let countBadge = this.lastMsgElement.find('.log-count'); if (countBadge.length === 0) { this.lastMsgElement.append(`x${this.lastMsgCount}`); } else { countBadge.text(`x${this.lastMsgCount}`); } this.Elements.LogList.prepend(this.lastMsgElement); return; } this.lastMsgContent = cleanMsg; this.lastMsgCount = 1; const timestamp = new Date().toLocaleTimeString('zh-TW', { hour12: false }); const entry = $(`
${timestamp} ${icon} ${cleanMsg}
`); this.lastMsgElement = entry; 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 條'); this.lastMsgContent = null; this.lastMsgElement = null; }, toggle: function(visible) { if (!this.Elements.Container) return; visible ? this.Elements.Container.fadeIn(200) : this.Elements.Container.fadeOut(200); $('#chk-ui-log').prop('checked', visible); } }; // ═══════════════════════════════════════════════════════════════ // 花園陣型可視化 (✅ v8.5.4-hotfix 修正:抽屜定位抖動問題) // ═══════════════════════════════════════════════════════════════ UI.GardenGrid = { Elements: { Container: null }, create: function() { if (this.Elements.Container) return; // 構建 DOM:核心三欄 + 底部清單 this.Elements.Container = $(` `); $('body').append(this.Elements.Container); this.Elements.Container.find('#garden-grid-close').click(() => this.toggle()); // 綁定按鈕 $('#cc-btn-expand-left').click(() => this.toggleSide('left')); $('#cc-btn-expand-right').click(() => this.toggleSide('right')); // 恢復記憶的狀態 if (Config.Memory.GardenLeftExpanded) this.toggleSide('left', true); if (Config.Memory.GardenRightExpanded) this.toggleSide('right', true); UI.makeDraggable(this.Elements.Container, 'gardenGridX', 'gardenGridY'); }, toggle: function() { if (!this.Elements.Container) this.create(); if (this.Elements.Container.is(':visible')) { this.Elements.Container.fadeOut(200); } else { this.update(); this.Elements.Container.fadeIn(200); } }, toggleSide: function(side, forceOpen = null) { const drawer = $(`#drawer-${side}`); const btn = $(`#cc-btn-expand-${side}`); const container = this.Elements.Container; // ✅ v8.5.4-hotfix 修正:不再修改 centerStage 寬度,只移動容器位置 const drawerWidth = 220; const currentX = parseInt(container.css('left')) || Config.Memory.GardenGridX; const isOpen = forceOpen !== null ? forceOpen : !drawer.hasClass('open'); if (isOpen) { // 開啟抽屜 if (side === 'left') { // 左抽屜開啟:左移面板防止Grid右跑 const newX = Math.max(0, currentX - drawerWidth); container.css('left', newX + 'px'); // ✅ 刪除 centerStage 寬度修改 } else { // 右抽屜開啟:保持位置,不修改寬度 // ✅ 刪除 centerStage 寬度修改 } drawer.addClass('open'); btn.text(side === 'left' ? '▶ 收起' : '收起 ◀'); Config.Memory[side === 'left' ? 'GardenLeftExpanded' : 'GardenRightExpanded'] = true; GM_setValue(side === 'left' ? 'gardenLeftExpanded' : 'gardenRightExpanded', true); // 保存新位置(左抽屜) if (side === 'left') { Config.Memory.GardenGridX = newX; GM_setValue('gardenGridX', newX); } } else { // 關閉抽屜 if (side === 'left') { // 左抽屜關閉:右移面板恢復位置 const newX = currentX + drawerWidth; container.css('left', newX + 'px'); // ✅ 刪除 centerStage 寬度修改 // 保存新位置 Config.Memory.GardenGridX = newX; GM_setValue('gardenGridX', newX); } else { // 右抽屜關閉:不修改寬度 // ✅ 刪除 centerStage 寬度修改 } drawer.removeClass('open'); btn.text(side === 'left' ? '◀ 展開' : '展開 ▶'); Config.Memory[side === 'left' ? 'GardenLeftExpanded' : 'GardenRightExpanded'] = false; GM_setValue(side === 'left' ? 'gardenLeftExpanded' : 'gardenRightExpanded', false); } }, update: function() { const plot = Config.Memory.SavedGardenPlot; const gridContent = $('#garden-grid-content').empty(); const unlockedList = $('#cc-garden-list-unlocked').empty(); const lockedList = $('#cc-garden-list-locked').empty(); const currentLayoutList = $('#cc-garden-current-layout').empty(); const emptyHint = $('#cc-garden-empty-hint'); let plantStats = {}; if (typeof Game !== 'undefined' && Game.Objects['Farm'].minigame) { const M = Game.Objects['Farm'].minigame; const totalPlants = 34; let unlockedCount = 0; // 1. 渲染網格 (v8.5.5-reborn:動態儀表板顏色邏輯) plot.forEach((row, rowIndex) => { row.forEach((cellId, colIndex) => { let bg = '#111'; let text = ''; let title = '空'; if (cellId > 0) { const plant = M.plantsById[cellId - 1]; if (plant) { // ✅ v8.5.5-reborn:動態顏色邏輯 const realTile = M.plot[rowIndex]?.[colIndex]?.[0] || 0; if (realTile === cellId) { bg = '#4caf50'; // 🟢 綠色:Real == Saved (正常) } else if (realTile === 0) { bg = '#2196f3'; // 🔵 藍色:Real == 0 (缺種子) } else if (realTile !== cellId) { bg = '#9c27b0'; // 🟣 紫色:Real != Saved (異常/變異) } else { bg = '#795548'; // ⚫ 深褐:其他情況 } text = cellId; title = plant.name; // 統計 if (!plantStats[cellId]) plantStats[cellId] = { name: plant.name, count: 0 }; plantStats[cellId].count++; } } else if (cellId === -1) { bg = '#222'; title = '未解鎖格子'; } gridContent.append(`
${text}
`); }); }); // 2. 左側:已解鎖 for (let i = 1; i <= totalPlants; i++) { const plant = M.plantsById[i]; if (plant && plant.unlocked) { unlockedCount++; unlockedList.append(`
  • [${plant.id}] ${plant.name}
  • `); } } if (unlockedCount === 0) unlockedList.append('
  • (無)
  • '); // 3. 右側:未解鎖 let lockedCount = 0; for (let i = 1; i <= totalPlants; i++) { const plant = M.plantsById[i]; if (plant && !plant.unlocked) { lockedCount++; lockedList.append(`
  • [${plant.id}] ${plant.name}
  • `); } } if (lockedCount === 0) lockedList.append('
  • ✓ 全解鎖!
  • '); // 4. 底部:當前陣型 const sortedIds = Object.keys(plantStats).map(Number).sort((a, b) => a - b); if (sortedIds.length > 0) { emptyHint.hide(); currentLayoutList.show(); sortedIds.forEach(id => { const data = plantStats[id]; currentLayoutList.append(`
  • [${id}] ${data.name}
    x${data.count}
  • `); }); } else { currentLayoutList.hide(); emptyHint.show(); } // 5. 進度文字 const progressColor = unlockedCount === totalPlants ? '#4caf50' : '#ffd700'; $('#cc-garden-progress').text(`${unlockedCount}/${totalPlants}`).css('color', progressColor); } else { gridContent.html('
    花園未加載
    '); emptyHint.show(); currentLayoutList.hide(); } // ✅ v8.5.5-reborn:綁定 tooltip 事件 this.bindTooltipEvents(); }, bindTooltipEvents: function() { $('#garden-grid-content > div').hover( // mouseenter function() { const plantId = $(this).data('plant-id'); if (plantId > 0 && typeof Game !== 'undefined' && Game.Objects['Farm'].minigame) { const M = Game.Objects['Farm'].minigame; const plant = M.plantsById[plantId - 1]; if (plant) { UI.Tooltip.show( this, plant.name, M.getCost(plant.id) ); } } }, // mouseleave function() { UI.Tooltip.hide(); } ); } }; // ═══════════════════════════════════════════════════════════════ // v8.5.5-reborn 新增:Rich Tooltip 模組 // ═══════════════════════════════════════════════════════════════ UI.Tooltip = { Elements: { Container: null }, create: function() { if (this.Elements.Container) return; this.Elements.Container = $(`
    `); $('body').append(this.Elements.Container); }, show: function(element, plantName, plantCost) { if (!this.Elements.Container) this.create(); const rect = element.getBoundingClientRect(); const affordable = Game.cookies >= plantCost; const priceColorClass = affordable ? 'price-affordable' : 'price-unaffordable'; const priceText = typeof Beautify !== 'undefined' ? Beautify(plantCost) : Math.round(plantCost).toLocaleString(); this.Elements.Container.html(`
    ${UI.cleanName(plantName)}
    價格: ${priceText} 🍪
    `); // 定位:顯示在元素右側 const tooltipWidth = this.Elements.Container.outerWidth(); const tooltipHeight = this.Elements.Container.outerHeight(); let left = rect.right + 10; let top = rect.top; // 避免超出視窗右側 if (left + tooltipWidth > window.innerWidth) { left = rect.left - tooltipWidth - 10; } // 避免超出視窗頂部/底部 if (top + tooltipHeight > window.innerHeight) { top = window.innerHeight - tooltipHeight - 10; } if (top < 10) { top = 10; } this.Elements.Container.css({ left: left + 'px', top: top + 'px', display: 'block' }); }, hide: function() { if (this.Elements.Container) { this.Elements.Container.hide(); } } }; // ═══════════════════════════════════════════════════════════════ // 花園保護模組 (✅ v8.5.5-reborn:新增嵌入式控制台) // ═══════════════════════════════════════════════════════════════ UI.GardenProtection = { Elements: { Container: null, EmbeddedControls: null // ✅ v8.5.5-reborn 新增:嵌入式控制台 }, 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) return; // 等待花園面板加載 const checkGardenPanel = () => { const gardenPanel = document.getElementById('gardenPanel'); if (!gardenPanel) { setTimeout(checkGardenPanel, 500); return; } this.createProtectionUI(gardenPanel); this.createEmbeddedControls(gardenPanel); // ✅ v8.5.5-reborn 新增 }; checkGardenPanel(); }, createProtectionUI: function(gardenPanel) { this.Elements.Container = $(` `); $(gardenPanel).append(this.Elements.Container); this.Elements.Container.find('#garden-prot-minimize').click(() => { this.minimize(); }); $('#spending-lock-label').hover( function() { $(this).css('background', 'rgba(255, 255, 255, 0.2)'); }, function() { $(this).css('background', 'rgba(255, 255, 255, 0.1)'); } ); $('#btn-save-garden-layout').hover( function() { $(this).css('background', '#1976d2'); }, function() { $(this).css('background', '#2196f3'); } ); $('#btn-show-grid').hover( function() { $(this).css('background', '#795548'); }, function() { $(this).css('background', '#8d6e63'); } ); this.bindEvents(); UI.makeDraggable(this.Elements.Container, 'gardenProtectionX', 'gardenProtectionY'); }, // ✅ v8.5.5-reborn 新增:創建嵌入式控制台 createEmbeddedControls: function(gardenPanel) { if (this.Elements.EmbeddedControls) return; this.Elements.EmbeddedControls = $(`
    `); $(gardenPanel).append(this.Elements.EmbeddedControls); // 綁定嵌入式按鈕事件 this.bindEmbeddedEvents(); // 根據當前狀態設置顯示/隱藏 this.updateEmbeddedVisibility(); }, bindEmbeddedEvents: function() { const self = this; // 恢復大面板 $('#btn-embed-restore').click(() => { this.restore(); }); // 鎖定支出 $('#btn-embed-lock').click(() => { if (!Config.Flags.SpendingLocked) { $('#chk-spending-lock').prop('checked', true).trigger('change'); Logger.log('嵌入式控制台', '已鎖定支出'); } }); // 解鎖支出 $('#btn-embed-unlock').click(() => { if (Config.Flags.SpendingLocked) { $('#chk-spending-lock').prop('checked', false).trigger('change'); Logger.log('嵌入式控制台', '已解鎖支出'); } }); // 記憶陣型 $('#btn-embed-save').click(() => { this.saveCurrentLayout(); }); // 顯示記憶網格 $('#btn-embed-show').click(() => { UI.GardenGrid.toggle(); }); }, updateEmbeddedVisibility: function() { if (!this.Elements.EmbeddedControls) return; if (Config.Memory.GardenProtectionMinimized) { this.Elements.EmbeddedControls.show(); } else { this.Elements.EmbeddedControls.hide(); } }, bindEvents: function() { $('#chk-spending-lock').change(function() { UI.GardenProtection.toggle(this.checked); }); $('#btn-save-garden-layout').click(function() { UI.GardenProtection.saveCurrentLayout(); }); $('#btn-show-grid').click(function() { UI.GardenGrid.toggle(); }); }, minimize: function() { // 隱藏大面板 if (this.Elements.Container) { this.Elements.Container.hide(); } // 顯示嵌入式控制台 if (this.Elements.EmbeddedControls) { this.Elements.EmbeddedControls.show(); } // 保存最小化狀態 Config.Memory.GardenProtectionMinimized = true; GM_setValue('gardenProtectionMinimized', true); Logger.log('花園保護', '面板已最小化(使用右側嵌入式控制台)'); }, restore: function() { // 隱藏嵌入式控制台 if (this.Elements.EmbeddedControls) { this.Elements.EmbeddedControls.hide(); } // 顯示大面板 if (this.Elements.Container && Config.Flags.ShowGardenProtection) { this.Elements.Container.show(); } // 保存恢復狀態 Config.Memory.GardenProtectionMinimized = false; GM_setValue('gardenProtectionMinimized', false); Logger.log('花園保護', '面板已恢復'); }, toggle: function(enabled, uiOnly = false) { if (enabled) { if (!uiOnly) { 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.Memory.SavedSpendingStates = { ...this.SavedStates }; GM_setValue('savedSpendingStates', Config.Memory.SavedSpendingStates); GM_setValue('spendingLocked', true); } // ✅ v8.5.2: 不關閉 Config.Flags.Buy,只關閉其他 Config.Flags.Garden = false; Config.Flags.Research = false; Config.Flags.Stock = false; // UI 視覺更新 const buyChk = $('#chk-auto-buy'); if (this.SavedStates.Buy) { buyChk.prop('checked', true).prop('disabled', true).css('opacity', '0.5').parent().attr('title', '資金保護中:僅允許購買誓約'); } else { buyChk.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(); if (!uiOnly) 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').parent().removeAttr('title'); $('#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(); if (!uiOnly) { GM_setValue('spendingLocked', false); this.SavedStates = { Buy: null, Garden: null, Research: null, Stock: null }; 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 || !this.Elements.EmbeddedControls) return; const gardenPanel = $('#gardenPanel'); const isGardenOpen = gardenPanel.length > 0 && gardenPanel.is(':visible'); if (isGardenOpen && Config.Flags.ShowGardenProtection) { if (Config.Memory.GardenProtectionMinimized) { // 最小化狀態:顯示嵌入式控制台,隱藏大面板 this.Elements.Container.hide(); this.Elements.EmbeddedControls.show(); } else { // 正常狀態:顯示大面板,隱藏嵌入式控制台 this.Elements.Container.fadeIn(200); this.Elements.EmbeddedControls.hide(); } } else { // 花園沒開,或是保護功能被禁用 -> 全部隱藏 this.Elements.Container.fadeOut(200); this.Elements.EmbeddedControls.hide(); } }, 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); Logger.success('花園保護', `花園陣型已記憶(${savedCount} 種種子)`); if (typeof Game !== 'undefined' && Game.Notify) Game.Notify('花園陣型已記憶', `已記錄 ${savedCount} 種種子`, [10, 6], 4); 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, 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) { 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) { document.querySelectorAll('#shimmers > div.shimmer').forEach(c => c.click()); } this.handleSpells(); }, 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(); 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', '觸發連擊:命運之手'); } } 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 { 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(); } } } }, GodzamokCombo: { update: function(now) { if (!Config.Flags.GlobalMasterSwitch || !Config.Flags.GodzamokCombo) return; if (now < Runtime.Timers.NextGodzamokCombo) return; const Temple = Game.Objects['Temple']; if (!Temple || !Temple.minigameLoaded) return; const M = Temple.minigame; if (M.slot[0] !== 2) return; let totalMult = 1; for (let i in Game.buffs) { totalMult *= (Game.buffs[i].multCpS * Game.buffs[i].multClick); } if (totalMult < Config.Settings.GodzamokMinMult) return; const targetName = Config.Settings.GodzamokTargetBuilding; const building = Game.Objects[targetName]; if (!building || building.amount < Config.Settings.GodzamokSellAmount) return; this.trigger(targetName, building, now); }, trigger: function(targetName, building, now) { const ALLOWED_BUILDINGS = ['Farm', 'Mine', 'Factory']; if (!ALLOWED_BUILDINGS.includes(targetName)) { Logger.error('Godzamok', `禁止賣出 ${targetName}!已攔截操作`); return false; } const costToBuyBack = building.price * Config.Settings.GodzamokSellAmount * 1.5; if (Game.cookies < costToBuyBack) { Logger.warn('Godzamok', `觸發失敗:資金不足以買回 (需 ${costToBuyBack})`); Runtime.Timers.NextGodzamokCombo = now + 5000; return; } Logger.log('Godzamok', `觸發連擊!倍率滿足條件`); if (!Config.Flags.SpendingLocked) { $('#chk-spending-lock').prop('checked', true).trigger('change'); } building.sell(Config.Settings.GodzamokSellAmount); Runtime.GodzamokState.soldAmount = Config.Settings.GodzamokSellAmount; Runtime.GodzamokState.isActive = true; setTimeout(() => { this.buyBack(targetName); }, Config.Settings.GodzamokBuyBackTime); Runtime.Timers.NextGodzamokCombo = now + Config.Settings.GodzamokCooldown; }, buyBack: function(targetName) { const building = Game.Objects[targetName]; const targetAmount = building.amount + Runtime.GodzamokState.soldAmount; Logger.log('Godzamok', `開始買回 ${targetName}...`); let bought = 0; let loops = 0; while (building.amount < targetAmount && building.price <= Game.cookies && loops < 1000) { building.buy(1); bought++; loops++; } if (bought >= Runtime.GodzamokState.soldAmount) { Logger.success('Godzamok', `已買回 ${bought} 座建築`); } else { Logger.warn('Godzamok', `資金不足/迴圈限制,僅買回 ${bought}/${Runtime.GodzamokState.soldAmount}`); } Runtime.GodzamokState.isActive = false; Runtime.GodzamokState.soldAmount = 0; if (Config.Flags.SpendingLocked) { $('#chk-spending-lock').prop('checked', false).trigger('change'); } } }, SugarLump: { update: function(now) { 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 = ''; switch (type) { case 3: statusText = '⛔ [肉色糖塊] 啟動保護:等待自然掉落'; statusColor = '#d32f2f'; borderColor = '#d32f2f'; action = 'SKIP'; break; case 2: case 4: if (age >= ripeAge) action = 'HARVEST_NOW'; else { statusText = `💎 [稀有糖塊] 等待成熟 (${UI.formatMs(ripeAge - age)})`; statusColor = '#e65100'; borderColor = '#ffd700'; } break; case 1: if (age >= ripeAge) { if ((Game.lumps / Config.Settings.SugarLumpGoal) > 0.9) action = 'HARVEST_NOW'; else action = 'HARVEST_NOW'; } else { statusText = `🌿 [雙倍糖塊] 等待成熟 (${UI.formatMs(ripeAge - age)})`; statusColor = '#2e7d32'; borderColor = '#4caf50'; } break; default: if (age >= ripeAge) action = 'HARVEST_NOW'; else { statusText = `🍬 [普通糖塊] 等待成熟 (${UI.formatMs(ripeAge - age)})`; statusColor = '#555'; borderColor = '#ccc'; } break; } if (action === 'HARVEST_NOW') { if (Config.Flags.GlobalMasterSwitch) { 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) { if (!Config.Flags.GlobalMasterSwitch) return; if (!Config.Flags.Buy || now < Runtime.Timers.NextBuy) return; if (typeof Game === 'undefined') return; // ⭐ 1. 誓約例外 (最高優先) 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. 智慧鎖定攔截 if (Config.Flags.SpendingLocked) return; // 3. 科技研發 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; } } // 4. 升級購買 let affordable = Game.UpgradesInStore.filter(u => u.canBuy()); affordable = affordable.filter(u => { if (u.id === 84) return false; if (u.id === 85) return false; if (u.id === 74) return false; 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; } // 5. 種子檢查 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; } } } // 6. 建築購買 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) { 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) { 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: { 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) { 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; if (this.checkSeedPriority()) { if (Math.random() < 0.05) Logger.log('Stock', '暫停交易:優先保留資金給花園種子'); Runtime.Timers.NextStock = now + 5000; 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) { 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) { 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 = { waitForGame: function(callback, maxRetries = 100) { 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.5-reborn Loading...'); // ✅ 1. 優先恢復鎖定狀態 (Fix Race Condition) const savedSpendingState = GM_getValue('spendingLocked', false); Config.Flags.SpendingLocked = savedSpendingState; if (savedSpendingState) { Logger.warn('Core', '偵測到支出鎖定狀態,已優先啟用保護'); const savedStates = GM_getValue('savedSpendingStates', null); if (savedStates) { UI.GardenProtection.SavedStates = savedStates; } } const scriptRestarted = localStorage.getItem('cookieScriptRestarted'); if (scriptRestarted) { Logger.log('Core', '腳本已自動重啟'); localStorage.removeItem('cookieScriptRestarted'); } UI.initStyles(); UI.createFloatingButton(); UI.createControlPanel(); UI.createCountdown(); UI.createBuffMonitor(); UI.ActionLog.create(); UI.GardenGrid.create(); UI.Tooltip.create(); // ✅ v8.5.5-reborn 新增 try { if (Game.setVolume) Game.setVolume(Config.Settings.Volume); } catch(e) { Logger.warn('Core', '音量設定失敗,遊戲可能未完全載入'); } this.scheduleRestart(); this.startHeartbeat(); setTimeout(() => { UI.GardenProtection.create(); const restoreLockState = () => { if ($('#chk-auto-buy').length === 0) { Logger.warn('花園保護', '主 UI 尚未就緒,延遲恢復狀態'); setTimeout(restoreLockState, 200); return; } if (Config.Flags.SpendingLocked) { $('#chk-spending-lock').prop('checked', true); UI.GardenProtection.toggle(true, true); } }; restoreLockState(); // ✅ v8.5.5-reborn:恢復最小化狀態 if (Config.Memory.GardenProtectionMinimized) { // 延遲執行以確保花園面板已加載 setTimeout(() => { UI.GardenProtection.minimize(); }, 1000); } }, 2000); setTimeout(() => { Logic.Garden.clearOverlay(); UI.updateButtonState(); UI.ActionLog.toggle(true); Logger.log('Core', '初始化完成,所有模組已就緒'); }, 3000); document.addEventListener('keydown', function(e) { if (e.key === 'F8') { e.preventDefault(); UI.toggleMasterSwitch(); } }); // ✅ Debug 接口:讓控制台可以訪問 window.CookieBot = { UI, Logic, Config, Core }; }, 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); Logic.Wrinkler.update(now); Logic.Stock.update(now); Logic.Season.update(now); Logic.Santa.update(now); Logic.GodzamokCombo.update(now); Logic.updateTitle(); Logic.Click.handlePrompts(); UI.updateBuffDisplay(); UI.GardenProtection.updateVisibility(); // ✅ v8.5.5-reborn 新增:生命週期守護 // 檢查嵌入式控制台是否因遊戲重繪而消失 if (Config.Memory.GardenProtectionMinimized && Config.Flags.ShowGardenProtection && $('#gardenPanel').is(':visible') && (!$('#cc-embed-right').length || $('#cc-embed-right').is(':hidden'))) { Logger.log('生命週期守護', '檢測到嵌入式控制台丟失,正在重建...'); // 嘗試重新創建嵌入式控制台 const gardenPanel = document.getElementById('gardenPanel'); if (gardenPanel && !$('#cc-embed-right').length) { UI.GardenProtection.createEmbeddedControls(gardenPanel); } else if ($('#cc-embed-right').is(':hidden')) { $('#cc-embed-right').show(); } } 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); } }; if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { Core.waitForGame(() => Core.init()); }); } else { Core.waitForGame(() => Core.init()); } })();