// ==UserScript== // @name Cookie Clicker Ultimate Automation // @name:zh-TW 餅乾點點樂全自動掛機輔助 (Cookie Clicker) // @name:zh-CN 饼干点点乐全自动挂机辅助 (Cookie Clicker) // @namespace http://tampermonkey.net/ // @version 8.8.5.3 // @description Automated clicker, auto-buy, auto-harvest, garden manager (5 slots), stock market, season manager, Santa evolver, Smart Sugar Lump harvester, Dragon Aura management, and the new Gambler feature. // @description:zh-TW 全功能自動掛機腳本 v8.8.5.3 Safety Patch:Logic.Season 聖誕判定修正 & 戰術核彈安全鎖 // @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== /* 變更日誌 (Changelog): v8.8.5.3 Safety Patch (2025): - [Logic] Logic.Season: 修正聖誕節判定,強制檢查馴鹿餅乾 (IDs 169-175) 收集狀況,防止過早畢業。 - [Logic] Logic.Season: 新增暖機期間阻斷,防止 F5 刷新瞬間的邏輯誤判。 - [Safety] GodzamokTactical: 新增萬神殿 Slot 0 檢查,防止在未裝備 Godzamok 時誤觸發核彈。 v8.8.5.2 Hotfix 2 (2025): - [Logic] Logic.Season.update 完全重構:採用「盤點優先、智慧跳過、零延遲」策略,優先計算庫存並在目標達成時立即跳至下一階段,修復季節切換卡死與順序邏輯問題。 v8.8.5.1 Hotfix (2025): - [Fix] 修正 Godzamok 戰術核彈設定 (買回數量/熱鍵) 的儲存與讀取邏輯。 - [UI] 調整戰術核彈按鈕位置 (Top 0px -> 10px) 以避免遮擋。 v8.8.5 (2025): - [Feature] 新增「戰術核彈 (Tactical Nuke)」模組:手動觸發 Godzamok 全賣 -> 買回戰術。 - [UI] 建築大師欄新增戰術核彈按鈕 (☢️)。 */ (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), DragonAura: GM_getValue('isDragonAuraEnabled', true), GardenOverlay: GM_getValue('isGardenOverlayEnabled', true), GardenAvoidBuff: GM_getValue('isGardenAvoidBuff', true), GardenMutation: GM_getValue('isGardenMutationEnabled', false), SyncPlanting: GM_getValue('isSyncPlantingEnabled', false), SavingMode: GM_getValue('isSavingModeEnabled', false), SavingReplant: GM_getValue('isSavingReplantEnabled', true), AutoPledge: GM_getValue('isAutoPledgeEnabled', true), 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', 0) * 60 + GM_getValue('buyIntervalSeconds', 10)) * 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, GardenBufferTime: 5000, GodzamokMinMult: 1000, GodzamokSellAmount: 100, GodzamokTargetBuilding: 'Farm', GodzamokCooldown: 15000, GodzamokBuyBackTime: 11000, GodzamokBuyback: GM_getValue('godzamokBuyback', 400), GodzamokHotkey: GM_getValue('godzamokHotkey', '^+F9') }, // 記憶 Memory: { SavedGardenPlot: Array(6).fill().map(() => Array(6).fill(-1)), GardenSavedPlots: GM_getValue('gardenSavedPlots', ['', '', '', '', '']), GardenSelectedSlot: GM_getValue('gardenSelectedSlot', 0), GardenSlotNames: GM_getValue('gardenSlotNames', ['Layout 1', 'Layout 2', 'Layout 3', 'Layout 4', 'Layout 5']), 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), 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 }), GardenProtectionMinimized: GM_getValue('gardenProtectionMinimized', false), LastBuffEndTime: GM_getValue('lastBuffEndTime', 0), SavingModeExpanded: GM_getValue('savingModeExpanded', false) } }; // 運行時計時器與緩存 const Runtime = { Cache: { BigCookie: null }, Timers: { NextBuy: 0, NextRestart: 0, NextGarden: 0, NextStock: 0, NextSeasonCheck: 0, NextGodzamokCombo: 0, GardenWarmup: 0 }, Stats: { ClickCount: 0, BuyUpgradeCount: 0, BuyBuildingCount: 0 }, GodzamokState: { isActive: false, soldAmount: 0, originalBuyState: true }, GodzamokTacticalState: { status: 'IDLE', // Enum: 'IDLE', 'COOLDOWN' lock: false // 防止重複觸發的鎖 }, DragonState: { isBursting: false, lastSwitchTime: 0, currentPhase: 'IDLE' }, ModuleFailCount: {}, OriginalTitle: document.title, SeasonState: { CurrentStage: 0, Roadmap: [ { id: 'valentines', name: 'Valentines', target: 'Normal' }, { id: 'christmas', name: 'Christmas', target: 'MaxSanta' }, { id: 'easter', name: 'Easter', target: 'Normal' }, { id: 'halloween', name: 'Halloween', target: 'Normal' } ], isFarming: false }, WarmupForceShown: false }; // ═══════════════════════════════════════════════════════════════ // 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, error) { const formatted = `[${module}] ${message}`; console.error(`❌ ${formatted}`, error || ''); if (UI.ActionLog && UI.ActionLog.append) UI.ActionLog.append(formatted, 'error'); if (typeof Game !== 'undefined' && Game.Notify) { Game.Notify('腳本錯誤', `${module}: ${message}`, [10, 6], 10); } }, 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'); }, critical: function(module, message) { const formatted = `[${module}] ⚠️ CRITICAL: ${message}`; console.error(`%c❌ ${formatted}`, 'color: #ff0000; font-weight: bold; background: #000; padding: 2px 5px;'); if (UI.ActionLog && UI.ActionLog.append) UI.ActionLog.append(formatted, 'error'); if (typeof Game !== 'undefined' && Game.Notify) { Game.Notify('功能已停用', `${module}: ${message}`, [10, 6], 10); } } }; // ═══════════════════════════════════════════════════════════════ // 1. UI 模組 // ═══════════════════════════════════════════════════════════════ const UI = { Elements: { Panel: null, FloatingBtn: null, Countdown: null, BuffMonitor: null }, _lastBuffSnapshot: null, getLocalizedPlantName: function(gridId) { if (!gridId || gridId === 0) return ''; const plantId = gridId - 1; if (typeof Game === 'undefined' || !Game.Objects['Farm'].minigame) return ''; const plant = Game.Objects['Farm'].minigame.plantsById[plantId]; return plant ? plant.name : ''; }, updateAllLayoutSelectors: function() { const selectors = [$('#gardenLayoutSelect'), $('#gardenLayoutSelectGrid')]; const plots = Config.Memory.GardenSavedPlots; const names = Config.Memory.GardenSlotNames; const selected = Config.Memory.GardenSelectedSlot; selectors.forEach(sel => { if(sel.length) { sel.empty(); for(let i=0; i<5; i++) { let isEmpty = true; try { if (plots[i] && plots[i] !== '[]' && plots[i] !== '') { const parsed = JSON.parse(plots[i]); if (parsed.length > 0) isEmpty = false; } } catch(e) {} let displayText = names[i]; if(isEmpty) displayText += ' (Empty)'; const opt = $('`; 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; const savingModeDisplay = Config.Memory.SavingModeExpanded ? 'block' : 'none'; const savingModeIcon = Config.Memory.SavingModeExpanded ? '▲' : '▼'; 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'); const currentBuffKeys = []; 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]; currentBuffKeys.push(`${buff.name}_${buff.time}`); if (buff.multCpS > 0) totalCpsMult *= buff.multCpS; if (buff.multClick > 0) totalClickMult *= buff.multClick; } } const needFullRefresh = !this._lastBuffSnapshot || this._lastBuffSnapshot.length !== currentBuffKeys.length || !this._lastBuffSnapshot.every((key, idx) => key === currentBuffKeys[idx]); if (needFullRefresh) { buffList.empty(); if (Game.buffs) { for (let i in Game.buffs) { const buff = Game.buffs[i]; 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); this._lastBuffSnapshot = currentBuffKeys; } else { if (Game.buffs) { let index = 0; for (let i in Game.buffs) { const buff = Game.buffs[i]; const timeElement = buffList.find('.buff-entry').eq(index).find('.buff-time'); if (timeElement.length) { timeElement.text(`剩餘: ${Math.ceil(buff.time / 30)}s`); } index++; } } } }, 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(); if(key==='GardenMutation') UI.GardenGrid.updateButtonState(); if(key==='DragonAura' && this.checked && Runtime.ModuleFailCount['Dragon'] >= 10) { Runtime.ModuleFailCount['Dragon'] = 0; Logger.success('Core', '已重置巨龍光環熔斷計數器'); } if(key==='SavingMode') self.updateEmbeddedState(); }); }; 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'); bindChk('chk-dragon', 'DragonAura'); $('#chk-main-sync').change(function() { const checked = this.checked; Config.Flags.SyncPlanting = checked; GM_setValue('isSyncPlantingEnabled', checked); $('#chk-garden-sync').prop('checked', checked); if(!checked) UI.GardenGrid.updateStatus('hide'); }); bindChk('chk-saving-mode', 'SavingMode'); bindChk('chk-saving-replant', 'SavingReplant'); bindChk('chk-auto-pledge', 'AutoPledge'); $('#header-saving-mode').click(function() { const content = $('#content-saving-mode'); const icon = $('#icon-saving-mode'); if (content.is(':visible')) { content.slideUp(200); icon.text('▼'); Config.Memory.SavingModeExpanded = false; } else { content.slideDown(200); icon.text('▲'); Config.Memory.SavingModeExpanded = true; } GM_setValue('savingModeExpanded', Config.Memory.SavingModeExpanded); }); $('#chk-finance-lock').change(function() { UI.GardenProtection.toggle(this.checked); UI.GardenProtection.updateEmbeddedState(); $('#chk-spending-lock').prop('checked', this.checked); self.updateEmbeddedState(); }); $('#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(); }); $('#btn-reset-ui-pos').click(() => { Config.Memory.GardenProtectionX = 100; Config.Memory.GardenProtectionY = 100; Config.Memory.GardenGridX = 100; Config.Memory.GardenGridY = 100; GM_setValue('gardenProtectionX', 100); GM_setValue('gardenProtectionY', 100); GM_setValue('gardenGridX', 100); GM_setValue('gardenGridY', 100); $('#garden-protection-ui').css({left: '100px', top: '100px'}); $('#garden-grid-panel').css({left: '100px', top: '100px'}); Logger.success('UI', '所有懸浮面板位置已重置'); }); $('#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); }); // [Fix 2] 重寫買回數量與熱鍵的事件監聽邏輯 $('#val-godzamok-buyback').change(function() { var raw = parseInt(this.value, 10); // 防呆:若非數字或小於-1,回退預設值 if (isNaN(raw) || raw < -1) raw = 400; Config.Settings.GodzamokBuyback = raw; this.value = raw; // 更新 UI 顯示標準化後的數值 GM_setValue('godzamokBuyback', raw); // 持久化 }); $('#val-godzamok-hotkey').change(function() { var val = this.value.trim(); if (!val) return; // 防空 Config.Settings.GodzamokHotkey = val; GM_setValue('godzamokHotkey', val); // 持久化 }); $('#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); } }; // ═══════════════════════════════════════════════════════════════ // 花園陣型可視化 // ═══════════════════════════════════════════════════════════════ UI.GardenGrid = { Elements: { Container: null }, create: function() { if (this.Elements.Container) return; this.Elements.Container = $(` `); $('body').append(this.Elements.Container); this.Elements.Container.find('#garden-grid-close').click(() => this.toggle()); $('#btn-grid-manual-refresh').click(() => UI.GardenGrid.update()); $('#btn-grid-toggle-mutation').click(() => { Config.Flags.GardenMutation = !Config.Flags.GardenMutation; GM_setValue('isGardenMutationEnabled', Config.Flags.GardenMutation); $('#chk-garden-mutation').prop('checked', Config.Flags.GardenMutation); this.updateButtonState(); }); $('#chk-garden-sync').change(function() { const checked = this.checked; Config.Flags.SyncPlanting = checked; GM_setValue('isSyncPlantingEnabled', checked); $('#chk-main-sync').prop('checked', checked); if (!checked) { UI.GardenGrid.updateStatus('hide'); } }); $('#gardenLayoutSelectGrid').change(function() { const newSlot = parseInt($(this).val()); Config.Memory.GardenSelectedSlot = newSlot; GM_setValue('gardenSelectedSlot', newSlot); // ✅ Safety Patch: Disable Mutation & Lock Spending Config.Flags.GardenMutation = false; GM_setValue('isGardenMutationEnabled', false); $('#chk-garden-mutation').prop('checked', false); UI.GardenGrid.updateButtonState(); Logger.warn('花園保護', '切換陣型:已自動關閉突變管理'); if (!Config.Flags.SpendingLocked) { $('#chk-spending-lock').prop('checked', true).trigger('change'); Logger.warn('花園保護', `[Grid] 切換至 Slot ${newSlot + 1},已啟用資金鎖定`); } Logic.Garden.loadLayout(); Logic.Garden.updateOverlay(); UI.GardenGrid.update(); UI.updateAllLayoutSelectors(); }); $('#btn-rename-layout').click(function() { const slot = Config.Memory.GardenSelectedSlot; const oldName = Config.Memory.GardenSlotNames[slot]; let newName = prompt('重新命名此陣型:', oldName); if (newName === null) return; newName = newName.trim(); if (newName === '') newName = `Layout ${slot + 1}`; if (newName.length > 20) newName = newName.substring(0, 20); Config.Memory.GardenSlotNames[slot] = newName; GM_setValue('gardenSlotNames', Config.Memory.GardenSlotNames); UI.updateAllLayoutSelectors(); Logger.success('UI', `已將 Slot ${slot+1} 重新命名為 "${newName}"`); }); $('#cc-btn-expand-left').click(() => this.toggleSide('left')); $('#cc-btn-expand-right').click(() => this.toggleSide('right')); $('#btn-expand-unlocked').click(function() { const c = $('#container-unlocked'); c.toggleClass('cc-garden-list-expanded'); $(this).text(c.hasClass('cc-garden-list-expanded') ? '▲' : '▼'); }); $('#btn-expand-locked').click(function() { const c = $('#container-locked'); c.toggleClass('cc-garden-list-expanded'); $(this).text(c.hasClass('cc-garden-list-expanded') ? '▲' : '▼'); }); if (Config.Memory.GardenLeftExpanded) this.toggleSide('left', true); if (Config.Memory.GardenRightExpanded) this.toggleSide('right', true); UI.makeDraggable(this.Elements.Container, 'gardenGridX', 'gardenGridY'); this.updateButtonState(); $(document).on('mouseenter', '#garden-grid-content > div', function() { const normalizedId = $(this).data('normalized-id'); if (typeof Game !== 'undefined' && Game.Objects['Farm'].minigame) { const M = Game.Objects['Farm'].minigame; let plant = null; if (normalizedId > -1) { plant = M.plantsById[normalizedId]; } const savedId = $(this).data('saved-id'); if (plant || savedId > -1) { const refPlant = plant || (savedId > -1 ? M.plantsById[savedId] : null); if (refPlant) { UI.Tooltip.show(this, refPlant, normalizedId, savedId); } } } }).on('mouseleave', '#garden-grid-content > div', function() { UI.Tooltip.hide(); }); }, updateButtonState: function() { const btn = $('#btn-grid-toggle-mutation'); if (btn.length) { if (Config.Flags.GardenMutation) { btn.text('🧬 突變管理: 開') .css({ 'background': '#2e7d32', 'border-color': '#4caf50' }); } else { btn.text('🧬 突變管理: 關') .css({ 'background': '#c62828', 'border-color': '#ff5252' }); } } }, updateStatus: function(type, value) { const barGrid = $('#sync-status-bar'); const barProt = $('#prot-sync-status'); if (!Config.Flags.SyncPlanting) { barGrid.hide(); barProt.hide(); return; } let bg = '', text = ''; let show = true; if (type === 'funds') { bg = '#ff9800'; text = `💰 資金不足: 缺 ${(value - Game.cookies).toLocaleString()}`; } else if (type === 'buff') { bg = '#9c27b0'; text = '⛔ 暫停: 等待 Buff 結束'; } else if (type === 'ready') { bg = '#4caf50'; text = '✅ 同步播種運行中'; } else { show = false; } if (show) { const style = { background: bg, display: 'block' }; barGrid.css(style).text(text); barProt.css(style).text(text); barProt.css('font-size', '11px'); } else { barGrid.hide(); barProt.hide(); } }, toggle: function() { if (!this.Elements.Container) this.create(); if (this.Elements.Container.is(':visible')) { this.Elements.Container.fadeOut(200); } else { this.update(); this.updateButtonState(); 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; const drawerWidth = 220; const currentX = parseInt(container.css('left')) || Config.Memory.GardenGridX; let newX = currentX; const isOpen = forceOpen !== null ? forceOpen : !drawer.hasClass('open'); if (isOpen) { if (side === 'left') { newX = Math.max(0, currentX - drawerWidth); container.css('left', newX + 'px'); Config.Memory.GardenGridX = newX; GM_setValue('gardenGridX', newX); } drawer.addClass('open'); btn.text(side === 'left' ? '▶ 收起' : '收起 ◀'); Config.Memory[side === 'left' ? 'GardenLeftExpanded' : 'GardenRightExpanded'] = true; GM_setValue(side === 'left' ? 'gardenLeftExpanded' : 'gardenRightExpanded', true); } else { if (side === 'left') { newX = currentX + drawerWidth; container.css('left', newX + 'px'); Config.Memory.GardenGridX = newX; GM_setValue('gardenGridX', newX); } drawer.removeClass('open'); btn.text(side === 'left' ? '◀ 展開' : '展開 ▶'); Config.Memory[side === 'left' ? 'GardenLeftExpanded' : 'GardenRightExpanded'] = false; GM_setValue(side === 'left' ? 'gardenLeftExpanded' : 'gardenRightExpanded', false); } }, update: function() { $('#garden-grid-content').empty(); const plot = Config.Memory.SavedGardenPlot; const gridContent = $('#garden-grid-content'); 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; for (let y = 0; y < 6; y++) { for (let x = 0; x < 6; x++) { const realTile = M.plot[y]?.[x] || [0, 0]; const gameId = realTile[0]; const normalizedId = (gameId === 0) ? -1 : gameId - 1; const savedId = plot[y][x]; let bg = '#111'; let stateClass = ''; let text = ''; let isUnlocked = M.isTileUnlocked(x, y); if (isUnlocked) { if (normalizedId > -1) { if (normalizedId === savedId) { bg = '#4caf50'; stateClass = 'cc-dashboard-correct'; text = normalizedId; } else { const plant = M.plantsById[normalizedId]; if (plant && plant.unlocked) { bg = '#f44336'; stateClass = 'cc-dashboard-weed'; } else { bg = '#9c27b0'; stateClass = 'cc-dashboard-new'; } text = normalizedId; } } else { if (savedId > -1) { bg = '#1565c0'; stateClass = 'cc-dashboard-missing'; text = savedId; } else { text = ''; } } } else { bg = '#000'; text = ''; } if (savedId > -1) { const localName = UI.getLocalizedPlantName(savedId + 1); if (localName) { if (!plantStats[savedId]) plantStats[savedId] = { name: localName, count: 0 }; plantStats[savedId].count++; } } gridContent.append(`
${text}
`); } } for (let i = 0; i < totalPlants; i++) { const plant = M.plantsById[i]; const localName = UI.getLocalizedPlantName(plant.id + 1); if (plant && plant.unlocked) { unlockedCount++; unlockedList.append(`
  • [${plant.id}] ${localName}
  • `); } } if (unlockedCount === 0) unlockedList.append('
  • (無)
  • '); let lockedCount = 0; for (let i = 0; i < totalPlants; i++) { const plant = M.plantsById[i]; const localName = UI.getLocalizedPlantName(plant.id + 1); if (plant && !plant.unlocked) { lockedCount++; lockedList.append(`
  • [${plant.id}] ${localName}
  • `); } } if (lockedCount === 0) lockedList.append('
  • ✓ 全解鎖!
  • '); 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(); } const progressColor = unlockedCount === totalPlants ? '#4caf50' : '#ffd700'; $('#cc-garden-progress').text(`${unlockedCount}/${totalPlants}`).css('color', progressColor); } else { gridContent.html('
    花園未加載
    '); emptyHint.show(); currentLayoutList.hide(); } } }; // ═══════════════════════════════════════════════════════════════ // 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, plant, normalizedId, savedId) { if (!this.Elements.Container) this.create(); const M = Game.Objects['Farm'].minigame; let displayPlant = plant; if (!displayPlant && savedId > -1) { displayPlant = M.plantsById[savedId]; } if (!displayPlant) { this.hide(); return; } const plantName = UI.cleanName(displayPlant.name); let price = 0; try { price = M.getCost(displayPlant); } catch(e) { price = 0; } const affordable = Game.cookies >= price; const priceText = typeof Beautify !== 'undefined' ? Beautify(price) : Math.round(price).toLocaleString(); let statusText = ''; let statusClass = ''; if (normalizedId === -1 && savedId > -1) { statusText = '🔵 缺種子'; statusClass = 'status-missing'; } else if (normalizedId === savedId) { statusText = '🟢 正確'; statusClass = 'status-correct'; } else if (normalizedId > -1) { const realPlant = M.plantsById[normalizedId]; if (realPlant) { if (realPlant.unlocked) { statusText = `🔴 異常: ${UI.cleanName(realPlant.name)}`; statusClass = 'status-weed'; } else { statusText = `🟣 變異: ${UI.cleanName(realPlant.name)}`; statusClass = 'status-new'; } } else { statusText = '🟣 異常'; statusClass = 'status-new'; } } else if (savedId === -1) { statusText = '⚫ 未設定'; statusClass = 'status-locked'; } this.Elements.Container.empty(); const $name = $('
    ').addClass('tooltip-name').text(plantName); const $priceVal = $('') .addClass(affordable ? 'price-affordable' : 'price-unaffordable') .text(priceText + ' 🍪'); const $priceDiv = $('
    ').addClass('tooltip-price') .append($('').text('價格: ')) .append($priceVal); const $status = $('
    ').addClass('tooltip-status ' + statusClass).text(statusText); this.Elements.Container.append($name).append($priceDiv).append($status); const rect = element.getBoundingClientRect(); 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(); } } }; // ═══════════════════════════════════════════════════════════════ // 花園保護模組 // ═══════════════════════════════════════════════════════════════ UI.GardenProtection = { Elements: { Container: null, EmbeddedControls: 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) return; const checkGardenPanel = () => { const gardenPlot = document.getElementById('gardenPlot'); if (!gardenPlot) { setTimeout(checkGardenPanel, 500); return; } this.createProtectionUI(gardenPlot); this.createEmbeddedControls(gardenPlot); }; 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.updateAllLayoutSelectors(); UI.makeDraggable(this.Elements.Container, 'gardenProtectionX', 'gardenProtectionY'); }, createEmbeddedControls: function(gardenPlot) { if (this.Elements.EmbeddedControls) return; this.Elements.EmbeddedControls = $(`
    `); $(gardenPlot).append(this.Elements.EmbeddedControls); this.bindEmbeddedEvents(); this.updateEmbeddedState(); this.updateEmbeddedVisibility(); }, bindEmbeddedEvents: function() { const self = this; $('#btn-embed-restore').click(() => { this.restore(); }); $('#btn-embed-toggle-lock').click(() => { $('#chk-spending-lock').prop('checked', !Config.Flags.SpendingLocked).trigger('change'); }); $('#btn-embed-save').click(() => { this.saveCurrentLayout(); }); $('#btn-embed-show').click(() => { UI.GardenGrid.toggle(); }); }, updateEmbeddedState: function() { const btn = $('#btn-embed-toggle-lock'); if (btn.length === 0) return; if (Config.Flags.SpendingLocked) { btn.html('鎖').css({ 'background': '#d32f2f', 'border-color': '#ffcdd2' }).attr('title', '目前已停止支出,點擊以恢復'); } else { btn.html('開').css({ 'background': '#388e3c', 'border-color': '#c8e6c9' }).attr('title', '目前允許支出,點擊以鎖定'); } }, 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); UI.GardenProtection.updateEmbeddedState(); }); $('#btn-save-garden-layout').click(function() { UI.GardenProtection.saveCurrentLayout(); }); $('#btn-show-grid').click(function() { UI.GardenGrid.toggle(); }); $('#gardenLayoutSelect').change(function() { const newSlot = parseInt($(this).val()); Config.Memory.GardenSelectedSlot = newSlot; GM_setValue('gardenSelectedSlot', newSlot); // ✅ Safety Patch: Disable Mutation & Lock Spending Config.Flags.GardenMutation = false; GM_setValue('isGardenMutationEnabled', false); $('#chk-garden-mutation').prop('checked', false); UI.GardenGrid.updateButtonState(); Logger.warn('花園保護', '切換陣型:已自動關閉突變管理'); if (!Config.Flags.SpendingLocked) { $('#chk-spending-lock').prop('checked', true).trigger('change'); Logger.warn('花園保護', `[Panel] 切換至 Slot ${newSlot + 1},已啟用資金鎖定`); } Logic.Garden.loadLayout(); Logic.Garden.updateOverlay(); if ($('#garden-grid-panel').is(':visible')) { UI.GardenGrid.update(); } UI.updateAllLayoutSelectors(); }); }, 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); } // ✅ Safety Patch: Disable Mutation when Locking Config.Flags.GardenMutation = false; GM_setValue('isGardenMutationEnabled', false); $('#chk-garden-mutation').prop('checked', false); UI.GardenGrid.updateButtonState(); Config.Flags.Garden = false; Config.Flags.Research = false; Config.Flags.Stock = false; 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; if ($('#chk-finance-lock').length) { $('#chk-finance-lock').prop('checked', enabled); } if (UI.updateEmbeddedState) UI.updateEmbeddedState(); }, 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'); const now = Date.now(); const warmupRemaining = Runtime.Timers.GardenWarmup - now; if (isGardenOpen && Config.Flags.ShowGardenProtection) { if (warmupRemaining > 0) { const container = this.Elements.Container; if (Config.Memory.GardenProtectionMinimized) { Runtime.WarmupForceShown = true; } container.show(); container.addClass('cc-warmup-shield'); this.Elements.EmbeddedControls.hide(); const title = container.find('span').first(); const remainingSeconds = Math.ceil(warmupRemaining / 1000); title.text(`🛡️ 暖機保護 (${remainingSeconds}s)`); } else { this.Elements.Container.removeClass('cc-warmup-shield'); const title = this.Elements.Container.find('span').first(); title.text('🛡️ 花園保護模式'); if (Runtime.WarmupForceShown) { this.minimize(); Runtime.WarmupForceShown = false; Logger.log('花園保護', '暖機結束,自動化已就緒'); } else { 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(); this.Elements.Container.removeClass('cc-warmup-shield'); } }, saveCurrentLayout: function() { Logic.Garden.saveLayout(); } }; // ═══════════════════════════════════════════════════════════════ // 2. 核心邏輯模組 (Business Logic) // ═══════════════════════════════════════════════════════════════ const Logic = { // ✅ Feature: Gambler (v8.8.3 Updated) Gambler: { spin: function() { const M = Game.Objects['Wizard tower'].minigame; if (!M) { Logger.error('Gambler', '魔法塔小遊戲尚未解鎖'); return; } const spellHoF = M.spells['hand of fate']; const spellStretch = M.spells['stretch time']; const cost = M.getSpellCost(spellHoF) + M.getSpellCost(spellStretch); if (M.magic < cost) { Logger.warn('Gambler', `魔力不足! 需要 ${Math.round(cost)} (當前: ${Math.round(M.magic)})`); this.updateLight('Red'); return; } Logger.log('Gambler', '🎲 命運輪盤轉動中...'); M.castSpell(spellHoF); // Auto-click the generated Golden Cookie if (Game.shimmers.length > 0) { const cookie = Game.shimmers[Game.shimmers.length - 1]; cookie.pop(); // Immediate click } else { Logger.error('Gambler', '找不到生成的餅乾!'); this.updateLight('Red'); return; } // Check Buff Result Strategy let bestBuff = null; // Priority Check: Dragonflight > Click frenzy > Others if (Game.buffs['Dragonflight']) bestBuff = Game.buffs['Dragonflight']; else if (Game.buffs['Click frenzy']) bestBuff = Game.buffs['Click frenzy']; else { // Fallback to searching all buffs for (let i in Game.buffs) { const buff = Game.buffs[i]; if (buff.multCpS > 7 || buff.multClick > 2) { bestBuff = buff; break; } } } if (!bestBuff) { Logger.log('Gambler', '結果: 爛牌 (無有效 Buff)'); this.updateLight('Red'); return; } // Determine Tier let tier = 'Low'; // Default if (bestBuff.name === 'Click frenzy' || bestBuff.name === 'Dragonflight') tier = 'High'; else if (bestBuff.name === 'Frenzy') tier = 'Mid'; if (tier === 'Low') { Logger.log('Gambler', `結果: 普通 (${bestBuff.name}), 跳過 Stretch`); this.updateLight('Red'); return; } // Execute Stretch Time const oldTime = bestBuff.time; M.castSpell(spellStretch); // Re-fetch buff to check time (Game objects update in place usually) let newTime = bestBuff.time; // Double check if buff still exists if (Game.buffs[bestBuff.name]) { newTime = Game.buffs[bestBuff.name].time; } const isSuccess = newTime > oldTime; let color = 'Red'; if (tier === 'High') { color = isSuccess ? 'Green' : 'Yellow'; Logger.success('Gambler', `✨ Jackpot! ${bestBuff.name} (${isSuccess ? '延長成功' : '縮短'})`); } else if (tier === 'Mid') { color = isSuccess ? 'Yellow' : 'Red'; Logger.log('Gambler', `✨ Mid-Tier: ${bestBuff.name} (${isSuccess ? '延長成功' : '縮短'})`); } this.updateLight(color); }, updateLight: function(color) { const light = $('#gambler-traffic-light'); if (light.length === 0) return; // Position Update Logic (v8.8.3) const btn = $('#statsButton'); if (btn.length > 0) { const rect = btn[0].getBoundingClientRect(); const top = rect.top + (rect.height / 2) - 30; // Center - Radius (30) const left = rect.right + 0; // Right side light.css({ 'top': top + 'px', 'left': left + 'px', 'display': 'block' }); } else { // Fallback center light.css({ 'top': '50%', 'left': '50%', 'transform': 'translate(-50%, -50%)', 'display': 'block' }); } let hex = '#000'; if (color === 'Green') hex = '#4caf50'; else if (color === 'Yellow') hex = '#ffeb3b'; else if (color === 'Red') hex = '#f44336'; light.css({ 'background': hex, 'box-shadow': `0 0 40px ${hex}`, 'opacity': '0.8' }); // Flash animation light.stop(true, true).fadeIn(100).fadeOut(100).fadeIn(100).fadeOut(100).fadeIn(100); // Auto hide after 3 seconds setTimeout(() => { light.animate({ opacity: 0 }, 500, function() { $(this).hide(); }); }, 3000); } }, Magic: { Timers: { NextSE: 0, NextCombo: 0 }, update: function(now) { if (!Config.Flags.GlobalMasterSwitch) return; const Tower = Game.Objects['Wizard tower']; if (!Tower || !Tower.minigameLoaded) return; const M = Tower.minigame; if (Config.Flags.Spell) { this.handleCombo(now, M); } if (Config.Flags.SE && now >= this.Timers.NextSE) { this.handleSE(now, M); } }, handleCombo: function(now, M) { if (now < this.Timers.NextCombo) return; let shouldCast = false; for (let i in Game.buffs) { const buff = Game.buffs[i]; const name = buff.name.toLowerCase(); if (name === 'click frenzy' || name === 'dragonflight' || name === 'cursed finger') { shouldCast = true; break; } if (buff.multCpS > 7 || buff.multClick > 10) { shouldCast = true; break; } } const spell = M.spells['hand of fate']; const spellCost = M.getSpellCost(spell); if (shouldCast && M.magic >= spellCost) { M.castSpell(spell); Logger.log('AutoSpell', '觸發連擊:施放 Hand of Fate'); this.Timers.NextCombo = now + 1000; } }, handleSE: function(now, M) { if (Object.keys(Game.buffs).length > 0) return; if (M.magic < M.magicM * 0.95) return; const spell = M.spells['spontaneous edifice']; const spellCost = M.getSpellCost(spell); if (M.magic < spellCost) return; const preCount = Game.BuildingsOwned; M.castSpell(spell); if (Game.BuildingsOwned > preCount) { Logger.success('AutoSpell', '閒置期:免費召喚了一座建築 (SE Success)'); this.Timers.NextSE = now + Config.Settings.SpellCooldownSuccess; } else { Logger.warn('AutoSpell', 'SE 施法無效 (條件不符),進入靜默模式 60s'); this.Timers.NextSE = now + Config.Settings.SpellCooldownFail; } } }, Click: { 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(); } } } }, Dragon: { State: Runtime.DragonState, Auras: { RadiantAppetite: 15, Dragonflight: 18, BreathOfMilk: 5 }, update: function(now) { if (!Config.Flags.GlobalMasterSwitch || !Config.Flags.DragonAura) return; if (typeof Game.dragonLevel === 'undefined' || Game.dragonLevel < 25) return; if (now - this.State.lastSwitchTime < 5000) return; let currentMult = 1; let hasClickBuff = false; for (let i in Game.buffs) { const b = Game.buffs[i]; if (b.multCpS > 0) currentMult *= b.multCpS; if (b.multClick > 0) currentMult *= b.multClick; if (b.name === 'Click frenzy' || b.name === 'Dragonflight') { hasClickBuff = true; } } const currentAura2 = Game.dragonAuras[1]; if (hasClickBuff) { if (this.State.currentPhase !== 'BURST') { const multDisplay = currentMult > 1000 ? (currentMult/1000).toFixed(1)+'k' : Math.round(currentMult); Logger.log('Dragon', `🔥 爆發開始 (倍率 x${multDisplay})。切換光環:飛龍 -> 牛奶`); this.State.currentPhase = 'BURST'; if (currentAura2 !== this.Auras.BreathOfMilk) this._setAura(1, this.Auras.BreathOfMilk); this.State.lastSwitchTime = now; } else { if (currentAura2 !== this.Auras.BreathOfMilk) { this._setAura(1, this.Auras.BreathOfMilk); this.State.lastSwitchTime = now; } } } else { if (this.State.currentPhase !== 'IDLE') { Logger.log('Dragon', `🎣 爆發結束。切換光環:牛奶 -> 飛龍`); this.State.currentPhase = 'IDLE'; if (currentAura2 !== this.Auras.Dragonflight) this._setAura(1, this.Auras.Dragonflight); this.State.lastSwitchTime = now; } else { if (currentAura2 !== this.Auras.Dragonflight) { this._setAura(1, this.Auras.Dragonflight); this.State.lastSwitchTime = now; } } } }, _setAura: function(slot, id) { try { if (typeof Game.setDragonAura === 'function') { Game.setDragonAura(slot, id); Game.recalculateGains = 1; } } catch(e) { Logger.error('Dragon', '光環切換失敗', e); } } }, 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 obj = Game.Objects[targetName]; if (!obj) { Logger.error('Godzamok', `目標建築 ${targetName} 不存在`); return false; } if (![2, 3, 4].includes(obj.id)) { Logger.error('Godzamok', `非法目標 ${targetName} (ID check failed)。僅允許 Farm, Mine, Factory。`); 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'); } } }, // ✅ Feature: Godzamok Tactical Nuke (v8.8.5) GodzamokTactical: { fire: function() { // New Safety Check (v8.8.5.3) const T = Game.Objects['Temple']; if (!T || !T.minigameLoaded || !T.minigame || T.minigame.slot[0] !== 2) { Logger.error('Tactical', '戰術中止:未裝備 Godzamok'); return; } if (Runtime.GodzamokTacticalState.lock) return; const targetName = Config.Settings.GodzamokTargetBuilding; const obj = Game.Objects[targetName]; // Whitelist check if (!obj || ![2, 3, 4].includes(obj.id)) { Logger.error('Tactical', `非法目標 ${targetName}。僅允許 Farm, Mine, Factory`); return; } Runtime.GodzamokTacticalState.lock = true; Runtime.GodzamokTacticalState.status = 'COOLDOWN'; UI.updateTacticalButton('ACTIVE'); const amount = obj.amount; obj.sell(amount); // Sell all using native API, strictly no loop Logger.log('Tactical', `☢️ 戰術核彈啟動!賣出 ${amount} 座 ${targetName}...`); // 10s delay setTimeout(() => { Logic.GodzamokTactical.reload(); }, 10000); }, reload: function() { const targetName = Config.Settings.GodzamokTargetBuilding; const obj = Game.Objects[targetName]; const setting = Config.Settings.GodzamokBuyback; const current = obj.amount; // Phase 3 - Buy Back if (setting === -1) { obj.buy(10000); // Buy max behavior } else if (setting > current) { const needed = setting - current; obj.buy(needed); } Runtime.GodzamokTacticalState.lock = false; Runtime.GodzamokTacticalState.status = 'IDLE'; UI.updateTacticalButton('IDLE'); Logger.log('Tactical', `🔄 戰術重裝完成`); } }, 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 || !Game) return; // 🛡️ [CONSTITUTION LEVEL 0]: 誓約與打寶虛擬鎖定 (Elder Pledge & Virtual Lock) const pledge = Game.Upgrades['Elder Pledge']; const isPledgeExpired = (typeof Game.pledgeT === 'undefined' || Game.pledgeT <= 0); if (Config.Flags.AutoPledge && pledge && pledge.unlocked && pledge.canBuy() && isPledgeExpired) { // 打寶模式:觸發虛擬鎖定,禁止所有支出(包括誓約) if (Runtime.SeasonState.isFarming) { // ⚠️ 關鍵註解,不得移除。QA依此註解進行靜態分析。 // [Constitution] Virtual Lock Block: Farming Mode. return; // 🔴 憲法級阻斷 } else { // 一般模式:強制購買誓約 pledge.buy(); return; // 🟢 購買後立即退出 } } // 🛡️ [CONSTITUTION REINFORCEMENT]: 二次虛擬鎖定檢查(防 Level 0 漏網) if (Runtime.SeasonState.isFarming) { return; // 🔴 憲法級阻斷 } // [Level 1]: 用戶緊急鎖定 (SpendingLocked) if (Config.Flags.SpendingLocked) return; // [Level 2]: 存錢模式 (SavingMode) if (Config.Flags.SavingMode) return; // [Level 3]: 常規購買邏輯 (僅當通過以上所有檢查後執行) if (!Config.Flags.Buy || now < Runtime.Timers.NextBuy) return; 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; } } 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; } 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]; if (seed && seed.unlocked) { needsSeeds = true; break outerLoop; } } } } } if (needsSeeds) { if (Math.random() < 0.1) Logger.log('Resource', '資金保護中:優先保留給花園種子'); return; } } } 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.AutoWrinkler) return; for (let i in Game.wrinklers) { let w = Game.wrinklers[i]; // 1. 保護閃光皺紋蟲 (Shiny, type === 1) if (w.type === 1) { if (!this.hasLoggedShiny) { Logger.success('Wrinkler', '發現閃光皺紋蟲!已啟動保護機制!'); this.hasLoggedShiny = true; } continue; // ✅ 絕對保護 } // 2. 策略選擇 if (Runtime.SeasonState.isFarming) { w.hp = 0; // 🔴 打寶模式:即時戳破 (Insta-Pop) } else { if (w.close === 1) w.hp = 0; // 🟡 一般模式:養肥後戳破 } } if (!Game.wrinklers.some(w => w.phase > 0 && w.type === 1)) { 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') return; const Farm = Game.Objects['Farm']; if (!Farm || !Farm.minigameLoaded) return; const M = Farm.minigame; if (!M) return; const isWarmup = Date.now() < Runtime.Timers.GardenWarmup; if (isWarmup) { if (Math.random() < 0.05) Logger.warn('花園保護', '暖機緩衝中:跳過鏟除操作'); Runtime.Timers.NextGarden = now + 2500; return; } let isCpsBuffActive = false; let shouldSkipPlanting = false; if (Config.Flags.GardenAvoidBuff) { for (let i in Game.buffs) { if (Game.buffs[i].multCpS > 1) { isCpsBuffActive = true; break; } } if (isCpsBuffActive) { Config.Memory.LastBuffEndTime = 0; } else { const currentTime = Date.now(); const buffEndTime = Config.Memory.LastBuffEndTime || 0; const bufferTime = Config.Settings.GardenBufferTime || 5000; if (currentTime - buffEndTime < bufferTime) { shouldSkipPlanting = true; } } } if (!isCpsBuffActive && Config.Memory.LastBuffEndTime === 0) { Config.Memory.LastBuffEndTime = Date.now(); GM_setValue('lastBuffEndTime', Config.Memory.LastBuffEndTime); } 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]; const normalizedId = (tileId === 0) ? -1 : tileId - 1; if (normalizedId > -1) { const plant = M.plantsById[normalizedId]; const isAnomaly = (savedId !== -1 && normalizedId !== savedId) || (savedId === -1); const plantName = UI.getLocalizedPlantName(tileId); 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); } } } } let allowPlanting = true; if (Config.Flags.SavingMode && !Config.Flags.SavingReplant) { allowPlanting = false; } if (allowPlanting) { if (Config.Flags.SyncPlanting) { let totalCost = 0; let missingPlants = []; 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 normalizedId = (tile[0] === 0) ? -1 : tile[0] - 1; const savedId = Config.Memory.SavedGardenPlot[y][x]; if (savedId > -1 && normalizedId === -1) { const seed = M.plantsById[savedId]; if (seed && seed.unlocked && M.canPlant(seed)) { totalCost += M.getCost(seed); missingPlants.push({x, y, id: seed.id}); } } } } if (Game.cookies < totalCost) { UI.GardenGrid.updateStatus('funds', totalCost); Runtime.Timers.NextGarden = now + 2500; return; } if (isCpsBuffActive) { UI.GardenGrid.updateStatus('buff'); Runtime.Timers.NextGarden = now + 2500; return; } if (missingPlants.length > 0) { UI.GardenGrid.updateStatus('ready'); for (let p of missingPlants) { M.useTool(p.id, p.x, p.y); } } else { UI.GardenGrid.updateStatus('ready'); } } else { UI.GardenGrid.updateStatus('hide'); 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 normalizedId = (tile[0] === 0) ? -1 : tile[0] - 1; const savedId = Config.Memory.SavedGardenPlot[y][x]; if (normalizedId === -1) { if (savedId !== -1 && savedId !== null) { const seed = M.plantsById[savedId]; if (seed && seed.unlocked && M.canPlant(seed)) { if (Config.Flags.GardenAvoidBuff && (isCpsBuffActive || shouldSkipPlanting)) 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 gameId = M.plot[y][x][0]; const normalizedId = (gameId === 0) ? -1 : gameId - 1; if (normalizedId === -1 && savedId !== -1) tileDiv.classList.add('cc-overlay-missing'); else if (normalizedId > -1) { const plant = M.plantsById[normalizedId]; const isAnomaly = (savedId !== -1 && normalizedId !== savedId) || (savedId === -1); if (isAnomaly) { if (plant.unlocked) tileDiv.classList.add('cc-overlay-anomaly'); else tileDiv.classList.add('cc-overlay-new'); } else if (normalizedId === 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 = []; 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 gameId = tile[0]; if (gameId === 0) { row.push(-1); } else { const plantIndex = gameId - 1; const plant = M.plantsById[plantIndex]; if (plant && plant.unlocked) { row.push(plantIndex); savedCount++; } else { row.push(-1); } } } else row.push(-1); } newLayout.push(row); } Config.Memory.SavedGardenPlot = newLayout; const currentSlot = Config.Memory.GardenSelectedSlot; const slotData = JSON.stringify(newLayout); Config.Memory.GardenSavedPlots[currentSlot] = slotData; GM_setValue('gardenSavedPlots', Config.Memory.GardenSavedPlots); const btn = $('#garden-save-btn'); const originalText = btn.text(); btn.text('✅ 已儲存!').css('background', '#4caf50'); UI.updateAllLayoutSelectors(); if (typeof Game !== 'undefined' && Game.Notify) Game.Notify('花園陣型已記憶', `已儲存至 Slot ${currentSlot + 1} (${savedCount} 種植物)`, [10, 6], 4); Logger.success('花園保護', `花園陣型已儲存至 Slot ${currentSlot + 1}`); setTimeout(() => btn.text(originalText).css('background', '#2196f3'), 1500); }, loadLayout: function() { const currentSlot = Config.Memory.GardenSelectedSlot; const slotString = Config.Memory.GardenSavedPlots[currentSlot]; try { if (slotString && slotString !== '' && slotString !== '[]') { const parsed = JSON.parse(slotString); if (Array.isArray(parsed) && parsed.length === 6) { Config.Memory.SavedGardenPlot = parsed; Logger.log('花園保護', `已載入陣型 Slot ${currentSlot + 1}`); } else { throw new Error('Invalid Format'); } } else { Config.Memory.SavedGardenPlot = Array(6).fill().map(() => Array(6).fill(-1)); Logger.log('花園保護', `Slot ${currentSlot + 1} 為空,已清除目標陣型`); } } catch(e) { Config.Memory.SavedGardenPlot = Array(6).fill().map(() => Array(6).fill(-1)); Logger.warn('花園保護', `Slot ${currentSlot + 1} 數據異常,已重置`); } } }, 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]; const normalizedId = (currentTile[0] === 0) ? -1 : currentTile[0] - 1; if (savedId > -1 && normalizedId === -1) { const seed = M.plantsById[savedId]; 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; if (Config.Flags.SpendingLocked) { Runtime.Timers.NextStock = now + 5000; 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 && !Config.Flags.SavingMode) { 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: { Requirements: { 'valentines': 7, 'christmas': 21, 'halloween': 7, 'easter': 20 }, update: function(now) { if (now < Runtime.Timers.GardenWarmup) return; if (!Config.Flags.GlobalMasterSwitch || !Config.Flags.Season) { if (Runtime.SeasonState.isFarming) Runtime.SeasonState.isFarming = false; return; } // 0. Boundary & Init const roadmap = Runtime.SeasonState.Roadmap; const stageIndex = Runtime.SeasonState.CurrentStage; if (stageIndex >= roadmap.length) { if (Runtime.SeasonState.isFarming) { Runtime.SeasonState.isFarming = false; Logger.success('Season', '全季節目標達成,自動化掛機中...'); } return; } if (now < Runtime.Timers.NextSeasonCheck) return; // 1. Lock Target const currentTask = roadmap[stageIndex]; const targetSeasonId = currentTask.id; const totalTarget = this.Requirements[targetSeasonId] || 0; // 2. Inventory Check let ownedCount = 0; for (let id in Game.UpgradesById) { const u = Game.UpgradesById[id]; if (u.season === targetSeasonId && u.bought) { ownedCount++; } } const isSantaReady = (targetSeasonId !== 'christmas') || (Game.santaLevel >= 14); if (targetSeasonId === 'christmas') { let allReindeer = true; for (let rid = 169; rid <= 175; rid++) { if (Game.UpgradesById[rid] && !Game.UpgradesById[rid].bought) { allReindeer = false; break; } } if (!allReindeer) ownedCount = 0; } // 3. Completion Check (Before Switch) if (ownedCount >= totalTarget && isSantaReady) { // ✅ 已畢業:執行智慧跳過 Logger.success('Season', `階段完成: ${currentTask.name} (收集 ${ownedCount}/${totalTarget})`); Runtime.SeasonState.CurrentStage++; Runtime.Timers.NextSeasonCheck = now; // 🚀 零延遲,立即檢查下一階 Runtime.SeasonState.isFarming = false; return; // ⛔ 阻斷後續邏輯,防止錯誤切換 } // 4. Switch Logic if (Game.season !== targetSeasonId) { const switcher = Object.values(Game.Upgrades).find(u => u.toggle && u.season === targetSeasonId); if (switcher && switcher.canBuy()) { Logger.log('Season', `切換季節至: ${currentTask.name} (進度: ${ownedCount}/${totalTarget})`); switcher.buy(); Runtime.Timers.NextSeasonCheck = now + 2000; } else { Runtime.Timers.NextSeasonCheck = now + 5000; } return; } // 5. Farming Logic Runtime.SeasonState.isFarming = true; const seasonUpgradesInStore = Game.UpgradesInStore.filter(u => u.season === targetSeasonId && u.canBuy()); if (seasonUpgradesInStore.length > 0) { seasonUpgradesInStore.forEach(u => { u.buy(); Logger.log('Season', `購買季節物品: ${u.name}`); }); Runtime.Timers.NextSeasonCheck = now + 500; } else { Runtime.Timers.NextSeasonCheck = now + 2000; } } }, 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 = { heartbeatTimer: null, 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秒),部分功能可能無法使用'); } }, safeExecute: function(fn, moduleName) { try { fn(); } catch(e) { Logger.error('Core', `${moduleName} 異常`, e); if (!Runtime.ModuleFailCount) Runtime.ModuleFailCount = {}; Runtime.ModuleFailCount[moduleName] = (Runtime.ModuleFailCount[moduleName] || 0) + 1; if (Runtime.ModuleFailCount[moduleName] >= 10) { Logger.critical('Core', `${moduleName} 連續失敗 10 次,已自動停用`); // Safety Disable switch(moduleName) { case 'Buy': Config.Flags.Buy = false; GM_setValue('isBuyEnabled', false); $('#chk-auto-buy').prop('checked', false); break; // ... (Other modules) } Runtime.ModuleFailCount[moduleName] = 0; } } }, init: function() { Logger.success('Core', 'Cookie Clicker Ultimate v8.8.5.3 Loading...'); const checkDataMigration = () => { const rawPlots = GM_getValue('gardenSavedPlots'); if (!rawPlots) { Logger.log('Migration', '偵測到舊版數據,正在升級資料結構...'); const newPlots = ['', '', '', '', '']; try { const oldData = GM_getValue('savedGardenPlot'); if (oldData && Array.isArray(oldData)) { newPlots[0] = JSON.stringify(oldData); Logger.success('Migration', '舊版陣型已遷移至 Slot 1'); } } catch(e) { Logger.error('Migration', '舊數據遷移失敗 (Fallback to Empty)', e); } GM_setValue('gardenSavedPlots', newPlots); Config.Memory.GardenSavedPlots = newPlots; } }; checkDataMigration(); Logic.Garden.loadLayout(); const safeDelay = Math.max(10000, Config.Settings.BuyIntervalMs); Runtime.Timers.NextBuy = Date.now() + safeDelay; Logger.log('Core', `自動購買已延遲 ${UI.formatMs(safeDelay)} 啟動,請利用此時間調整設定`); Runtime.Timers.GardenWarmup = Date.now() + 10000; Logger.log('Core', '[花園保護] 暖機模式啟動:暫停操作 10 秒'); 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(); // Initial UI Creation UI.createRightControls(); UI.createTrafficLight(); 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); UI.GardenProtection.updateEmbeddedState(); if (UI.updateEmbeddedState) UI.updateEmbeddedState(); } }; restoreLockState(); if (Config.Memory.GardenProtectionMinimized) { setTimeout(() => { UI.GardenProtection.minimize(); UI.GardenProtection.updateEmbeddedState(); }, 1000); } }, 2000); setTimeout(() => { Logic.Garden.clearOverlay(); UI.updateButtonState(); UI.ActionLog.toggle(true); Logger.log('Core', '初始化完成,所有模組已就緒'); }, 3000); // Input Listener System (v8.8.5) document.addEventListener('keydown', function(e) { // Toggle Master Switch if (e.key === 'F8') { e.preventDefault(); UI.toggleMasterSwitch(); } // Godzamok Tactical Nuke Hotkey Parsing const setting = Config.Settings.GodzamokHotkey; if (setting) { const wantCtrl = setting.includes('^'); const wantShift = setting.includes('+'); const wantAlt = setting.includes('!'); if (e.ctrlKey === wantCtrl && e.shiftKey === wantShift && e.altKey === wantAlt) { let mainKey = setting.replace(/[\^\+\!]/g, '').toUpperCase(); if (e.key.toUpperCase() === mainKey) { e.preventDefault(); e.stopPropagation(); Logic.GodzamokTactical.fire(); } } } }); window.CookieBot = { UI, Logic, Config, Core, Runtime }; }, startHeartbeat: function() { const self = this; const runClicker = () => { // [Fast Loop] 嚴禁 DOM 查詢,只點擊 Cache if (Config.Flags.GlobalMasterSwitch && Config.Flags.Click && Runtime.Cache.BigCookie) { try { Runtime.Cache.BigCookie.click(); Runtime.Stats.ClickCount++; } catch(e) {} } setTimeout(runClicker, Config.Flags.Click ? Math.max(10, Config.Settings.ClickInterval) : 1000); }; // [Loop 3] Heartbeat (慢速/DOM/維護) this.heartbeatTimer = setInterval(() => { const now = Date.now(); // ✅ 正確順序(不可更改): Logic.Season.update(now); // 1. 設定 `isFarming` 狀態 Logic.Wrinkler.update(now); // 2. 執行皺紋蟲策略 Logic.Buy.update(now); // 3. 執行購買邏輯(依賴 `isFarming`) Logic.Garden.update(now); // 4. 其他模組... Logic.Magic.update(now); // 1. 維護 Cache (允許 DOM) const newCookie = document.querySelector('#bigCookie'); Runtime.Cache.BigCookie = newCookie || null; // 2. 金餅乾點擊 if (Config.Flags.Golden) { document.querySelectorAll('#shimmers > div.shimmer').forEach(c => c.click()); } if (!Config.Flags.GlobalMasterSwitch) return; // 3. 執行所有邏輯 (移入低速迴圈) self.safeExecute(() => Logic.SugarLump.update(now), 'SugarLump'); self.safeExecute(() => Logic.Stock.update(now), 'Stock'); self.safeExecute(() => Logic.Santa.update(now), 'Santa'); self.safeExecute(() => Logic.Dragon.update(now), 'Dragon'); self.safeExecute(() => Logic.GodzamokCombo.update(now), 'GodzamokCombo'); try { Logic.Garden.updateOverlay(); } catch(e) {} // 4. UI 更新 if ($('#chk-auto-pledge').length) { const label = $('#chk-auto-pledge').parent().find('span'); if (Runtime.SeasonState.isFarming) { label.html('🔄 自動誓約 (打寶中:暫停購買)'); } else { label.text('🔄 自動購買長者誓約'); } } try { Logic.updateTitle(); } catch(e) {} try { Logic.Click.handlePrompts(); } catch(e) {} try { UI.updateBuffDisplay(); } catch(e) { console.error(e); } try { UI.GardenProtection.updateVisibility(); } catch(e) {} // Grimoire & Tactical Button Injection (Dynamic) try { UI.createGrimoireControls(); } catch(e) {} try { UI.createTacticalButton(); } catch(e) {} if (Config.Flags.ShowGardenGrid && $('#garden-grid-panel').is(':visible')) { try { UI.GardenGrid.update(); } catch(e) {} } const gardenPanel = document.getElementById('gardenPanel'); if (gardenPanel && gardenPanel.style.display !== 'none') { const embedRight = document.getElementById('cc-embed-right'); if (Config.Memory.GardenProtectionMinimized && Config.Flags.ShowGardenProtection) { if (!embedRight || embedRight.style.display === 'none') { const gardenPlot = document.getElementById('gardenPlot'); if (gardenPlot && !embedRight) { UI.GardenProtection.createEmbeddedControls(gardenPlot); UI.GardenProtection.updateEmbeddedState(); } else if (embedRight && embedRight.style.display === 'none') { embedRight.style.display = 'flex'; } } } } if ($('#cc-embedded-controls').length === 0) { UI.createRightControls(); } 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); // 啟動分離的迴圈 runClicker(); }, 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'); GM_openInTab(window.location.href, { active: true, insert: true, setParent: false }); if (this.heartbeatTimer) clearInterval(this.heartbeatTimer); setTimeout(() => { window.close(); document.body.innerHTML = `

    🔄 系統已重啟

    新分頁已開啟,請手動關閉此分頁以節省資源。

    (瀏覽器安全設定攔截了自動關閉功能)

    `; document.title = "⛔ 請關閉此分頁"; }, 100); } }; if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', () => { Core.waitForGame(() => Core.init()); }); } else { Core.waitForGame(() => Core.init()); } })();