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