Automated clicker, auto-buy, auto-harvest, garden manager, stock market, season manager, and Santa evolver.
当前为
// ==UserScript==
// @name Cookie Clicker Ultimate Automation v8.0 (Reborn)
// @name:zh-TW 餅乾點點樂全自動掛機輔助 v8.0 (架構重構版)
// @namespace http://tampermonkey.net/
// @version 8.0
// @description Automated clicker, auto-buy, auto-harvest, garden manager, stock market, season manager, and Santa evolver.
// @description:zh-TW 全功能自動掛機腳本 v8.0:新增季節自動切換、聖誕老人自動進化、全新模組化架構。
// @author You & AI Architect
// @match https://wws.justnainai.com/*
// @match https://orteil.dashnet.org/cookieclicker/*
// @icon https://orteil.dashnet.org/cookieclicker/img/favicon.ico
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_openInTab
// @require https://code.jquery.com/jquery-3.6.0.min.js
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// ═══════════════════════════════════════════════════════════════
// 0. 全域配置與狀態 (Configuration & State)
// ═══════════════════════════════════════════════════════════════
const Config = {
// 開關狀態
Flags: {
Click: GM_getValue('isClickEnabled', false),
Buy: GM_getValue('isBuyEnabled', false),
Golden: GM_getValue('isGoldenEnabled', false),
Spell: GM_getValue('isSpellEnabled', true),
Garden: GM_getValue('isGardenEnabled', false),
Research: GM_getValue('isResearchEnabled', true),
Stock: GM_getValue('isStockEnabled', false),
SE: GM_getValue('isSEEnabled', false),
Season: GM_getValue('isSeasonEnabled', true), // v8.0 New
Santa: GM_getValue('isSantaEnabled', true), // v8.0 New
// 花園細項
GardenOverlay: GM_getValue('isGardenOverlayEnabled', false),
GardenAvoidBuff: GM_getValue('isGardenAvoidBuff', true),
GardenMutation: GM_getValue('isGardenMutationEnabled', false),
// UI
ShowCountdown: GM_getValue('showCountdown', true),
ShowBuffMonitor: GM_getValue('showBuffMonitor', true)
},
// 參數
Settings: {
Volume: GM_getValue('gameVolume', 50),
ClickInterval: GM_getValue('clickInterval', 50),
BuyStrategy: GM_getValue('buyStrategy', 'expensive'),
BuyIntervalMs: (GM_getValue('buyIntervalHours', 0) * 3600 + GM_getValue('buyIntervalMinutes', 3) * 60 + GM_getValue('buyIntervalSeconds', 0)) * 1000,
RestartIntervalMs: (GM_getValue('restartIntervalHours', 1) * 3600 + GM_getValue('restartIntervalMinutes', 0) * 60 + GM_getValue('restartIntervalSeconds', 0)) * 1000,
MaxWizardTowers: 800
},
// 記憶
Memory: {
SavedGardenPlot: GM_getValue('savedGardenPlot', Array(6).fill().map(() => Array(6).fill(-1))),
PanelX: GM_getValue('panelX', window.innerWidth / 2 - 200),
PanelY: GM_getValue('panelY', 100),
BuffX: GM_getValue('buffX', window.innerWidth - 340),
BuffY: GM_getValue('buffY', 150),
CountdownX: GM_getValue('countdownX', window.innerWidth - 170),
CountdownY: GM_getValue('countdownY', 10),
ButtonX: GM_getValue('buttonX', 50),
ButtonY: GM_getValue('buttonY', 50)
}
};
// 運行時計時器與緩存
const Runtime = {
Timers: {
NextBuy: 0,
NextRestart: 0,
NextGarden: 0,
NextStock: 0,
NextSeasonCheck: 0
},
Stats: {
ClickCount: 0,
BuyUpgradeCount: 0,
BuyBuildingCount: 0
},
OriginalTitle: document.title,
SeasonState: {
CurrentStage: 0,
// 季節路線圖 (Season Roadmap)
Roadmap: [
{ name: 'Valentine', id: 'fools', target: 'BuyAllUpgrades' }, // 情人節 (內部ID fools 即 valentines)
{ name: 'Christmas', id: 'christmas', target: 'MaxSanta' } // 聖誕節 + 聖誕老人
]
}
};
// ═══════════════════════════════════════════════════════════════
// 1. UI 與 日誌模組 (UI & Logging)
// ═══════════════════════════════════════════════════════════════
const UI = {
Elements: {
Panel: null,
FloatingBtn: null,
Countdown: null,
BuffMonitor: null
},
initStyles: function() {
const style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = `
.cc-overlay-missing { border: 3px dashed #2196f3 !important; box-sizing: border-box; background: rgba(33, 150, 243, 0.1); }
.cc-overlay-anomaly { border: 3px solid #ff4444 !important; box-shadow: inset 0 0 15px rgba(255, 0, 0, 0.6) !important; box-sizing: border-box; z-index: 10; }
.cc-overlay-new { border: 3px solid #9c27b0 !important; box-shadow: inset 0 0 15px rgba(156, 39, 176, 0.8), 0 0 10px rgba(156, 39, 176, 0.5) !important; box-sizing: border-box; z-index: 12; }
.cc-overlay-correct { border: 1px solid rgba(76, 175, 80, 0.4) !important; box-sizing: border-box; }
`;
document.head.appendChild(style);
},
// 格式化時間輔助
formatMs: function(ms) {
const s = Math.floor(ms/1000);
return `${Math.floor(s/60)}:${s%60<10?'0':''}${s%60}`;
},
// 清洗名稱輔助
cleanName: function(str) {
if (!str) return '';
if (typeof str !== 'string') return String(str);
return str.replace(/<[^>]*>/g, '').trim();
},
// 創建懸浮按鈕
createFloatingButton: function() {
if (this.Elements.FloatingBtn) return;
this.Elements.FloatingBtn = $(`
<div id="cookie-floating-button" style="
position: fixed; left: ${Config.Memory.ButtonX}px; top: ${Config.Memory.ButtonY}px; width: 50px; height: 50px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 50%;
display: flex; align-items: center; justify-content: center; cursor: pointer; z-index: 999999;
font-size: 24px; box-shadow: 0 4px 15px rgba(0,0,0,0.3); transition: filter 0.3s;
">🍪</div>
`);
this.Elements.FloatingBtn.click((e) => { e.stopPropagation(); this.togglePanel(); });
let isD = false;
this.Elements.FloatingBtn.mousedown(() => isD = true);
$(document).mousemove((e) => {
if(isD) {
Config.Memory.ButtonX = e.clientX - 25;
Config.Memory.ButtonY = e.clientY - 25;
this.Elements.FloatingBtn.css({left: Config.Memory.ButtonX, top: Config.Memory.ButtonY});
}
}).mouseup(() => {
if(isD) {
isD = false;
GM_setValue('buttonX', Config.Memory.ButtonX);
GM_setValue('buttonY', Config.Memory.ButtonY);
}
});
$('body').append(this.Elements.FloatingBtn);
this.updateButtonState();
},
updateButtonState: function() {
if (this.Elements.FloatingBtn) {
this.Elements.FloatingBtn.css('filter', Config.Flags.Click ? 'hue-rotate(0deg)' : 'grayscale(100%)');
}
},
// 創建控制面板
createControlPanel: function() {
if (this.Elements.Panel) return;
const generateOptions = (min, max, sel, unit) => {
let h=''; for(let i=min; i<=max; i++) h+=`<option value="${i}" ${i === sel?'selected':''}>${i}${unit}</option>`; return h;
};
// 分解 Config.Settings.BuyIntervalMs 為 分/秒 供 UI 顯示
const buyMin = Math.floor(Config.Settings.BuyIntervalMs / 60000);
const buySec = (Config.Settings.BuyIntervalMs % 60000) / 1000;
// 分解 RestartIntervalMs
const rstHr = Math.floor(Config.Settings.RestartIntervalMs / 3600000);
const rstMin = (Config.Settings.RestartIntervalMs % 3600000) / 60000;
this.Elements.Panel = $(`
<div id="cookie-control-panel" style="
position: fixed; left: ${Config.Memory.PanelX}px; top: ${Config.Memory.PanelY}px; width: 420px;
max-height: 85vh; background: #fff; border-radius: 12px;
box-shadow: 0 10px 40px rgba(0,0,0,0.4); z-index: 999998;
font-family: Arial, sans-serif; display: none; overflow: hidden; color: #333;
">
<div id="panel-header" style="
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white; padding: 15px; font-weight: bold; font-size: 18px;
cursor: move; display: flex; justify-content: space-between; align-items: center;
">
<span>🍪 控制面板 v8.0 (Reborn)</span>
</div>
<div style="padding: 20px; overflow-y: auto; max-height: calc(85vh - 60px);">
<div class="panel-section" style="margin-bottom:15px; padding:10px; background:#f5f7fa; border-radius:8px;">
<div style="font-weight:bold; color:#222; margin-bottom:10px; border-bottom:2px solid #ddd; padding-bottom:5px;">🎛️ 核心模組</div>
<div style="display:grid; grid-template-columns: 1fr 1fr; gap:10px; color:#333;">
<label><input type="checkbox" id="chk-auto-click" ${Config.Flags.Click?'checked':''}> 👉 自動點擊</label>
<label><input type="checkbox" id="chk-auto-buy" ${Config.Flags.Buy?'checked':''}> 🛒 自動購買</label>
<label><input type="checkbox" id="chk-auto-golden" ${Config.Flags.Golden?'checked':''}> ⭐ 金餅乾/糖塊</label>
<label><input type="checkbox" id="chk-auto-garden" ${Config.Flags.Garden?'checked':''}> 🌻 花園維護</label>
</div>
</div>
<div class="panel-section" style="margin-bottom:15px; padding:10px; background:#e1f5fe; border-radius:8px;">
<div style="font-weight:bold; color:#0277bd; margin-bottom:5px;">📈 進階掛機</div>
<div style="color:#333; font-size:13px; display:grid; gap:8px;">
<label><input type="checkbox" id="chk-stock" ${Config.Flags.Stock?'checked':''}> 股市自動交易</label>
<label><input type="checkbox" id="chk-se" ${Config.Flags.SE?'checked':''}> 🧙♂️ 閒置魔法: 憑空建築</label>
<label><input type="checkbox" id="chk-season" ${Config.Flags.Season?'checked':''}> 🍂 季節管理 (v8.0)</label>
<label><input type="checkbox" id="chk-santa" ${Config.Flags.Santa?'checked':''}> 🎅 聖誕老人進化 (v8.0)</label>
</div>
</div>
<div class="panel-section" style="margin-bottom:15px; padding:10px; background:#e8f5e9; border-radius:8px;">
<div style="font-weight:bold; color:#1b5e20; margin-bottom:5px;">🌻 花園自動化</div>
<label style="display:block; margin-bottom:8px; font-weight:bold; color:#2e7d32;">
<input type="checkbox" id="chk-garden-overlay" ${Config.Flags.GardenOverlay?'checked':''}> [x] 顯示陣型輔助網格
</label>
<label style="display:block; margin-bottom:8px; font-weight:bold; color:#d84315;">
<input type="checkbox" id="chk-garden-mutation" ${Config.Flags.GardenMutation?'checked':''}> 🧬 啟用自動突變管理
</label>
<label style="display:block; margin-bottom:8px; font-weight:bold; color:#1565c0;">
<input type="checkbox" id="chk-garden-smart" ${Config.Flags.GardenAvoidBuff?'checked':''}> 🧠 聰明補種 (Buff 期間暫停)
</label>
<button id="garden-save-btn" style="
width:100%; padding:8px; background:#2196f3; color:white; border:none; border-radius:4px; cursor:pointer;
">💾 記憶當前陣型</button>
</div>
<div class="panel-section" style="margin-bottom:15px; padding:10px; background:#fff3e0; border-radius:8px;">
<div style="font-weight:bold; color:#e65100; margin-bottom:5px;">🛒 購買策略</div>
<select id="buy-strategy" style="width:100%; padding:5px;">
<option value="expensive" ${Config.Settings.BuyStrategy==='expensive'?'selected':''}>最貴優先</option>
<option value="cheapest" ${Config.Settings.BuyStrategy==='cheapest'?'selected':''}>最便宜優先</option>
</select>
<div style="display:flex; gap:5px; align-items:center; margin-top:5px; color:#333;">
<span style="font-size:13px;">間隔:</span>
<select id="buy-min">${generateOptions(0, 59, buyMin, '分')}</select>
<select id="buy-sec">${generateOptions(0, 59, buySec, '秒')}</select>
</div>
</div>
<div class="panel-section" style="margin-bottom:10px; padding:10px; background:#f3e5f5; border-radius:8px;">
<div style="font-weight:bold; color:#4a148c; margin-bottom:5px;">⚙️ 其他設定</div>
<label style="display:block; font-size:13px; color:#333;"><input type="checkbox" id="chk-spell" ${Config.Flags.Spell?'checked':''}> 🧙♂️ 基礎魔法連擊</label>
<label style="display:block; font-size:13px; color:#333;"><input type="checkbox" id="chk-ui-count" ${Config.Flags.ShowCountdown?'checked':''}> ⏱️ 倒數計時</label>
<label style="display:block; font-size:13px; color:#333;"><input type="checkbox" id="chk-ui-buff" ${Config.Flags.ShowBuffMonitor?'checked':''}> 🔥 Buff 監控</label>
<div style="margin-top:12px; color:#333;">
<span style="font-size:13px; font-weight:bold;">點擊速度: <span id="spd-val">${Config.Settings.ClickInterval}</span>ms</span>
<input type="range" id="spd-slider" min="10" max="200" value="${Config.Settings.ClickInterval}" style="width:100%; margin-top: 8px;">
</div>
<div style="margin-top:10px; border-top:1px solid #ccc; padding-top:8px; color:#333;">
<span style="font-size:13px;">自動重啟:</span>
<select id="rst-hr">${generateOptions(0, 24, rstHr, '時')}</select>
<select id="rst-min">${generateOptions(0, 59, rstMin, '分')}</select>
<button id="btn-force-restart" style="float:right; background:#ff5252; color:white; border:none; padding:4px 10px; border-radius:4px; cursor:pointer;">立即重啟</button>
</div>
</div>
</div>
</div>
`);
this.makeDraggable(this.Elements.Panel, 'panelX', 'panelY', '#panel-header');
$('body').append(this.Elements.Panel);
this.bindEvents();
},
togglePanel: function() {
if (!this.Elements.Panel) this.createControlPanel();
this.Elements.Panel.is(':visible') ? this.Elements.Panel.fadeOut(200) : this.Elements.Panel.fadeIn(200);
},
createCountdown: function() {
if (this.Elements.Countdown) return;
this.Elements.Countdown = $(`
<div id="cookie-countdown" style="
position: fixed; left: ${Config.Memory.CountdownX}px; top: ${Config.Memory.CountdownY}px; padding: 8px; background: rgba(0,0,0,0.85); color: white;
border-radius: 8px; font-family: monospace; font-size: 12px; z-index: 999997; display: ${Config.Flags.ShowCountdown ? 'block' : 'none'};
width: 150px; cursor: move; border: 1px solid #444;
">
<div style="text-align: center; border-bottom: 1px solid #555; margin-bottom: 4px;">⏱️ 倒數計時 v8.0</div>
<div style="display:flex; justify-content:space-between;"><span>🔄 重啟:</span><span id="txt-rst">--:--</span></div>
<div style="display:flex; justify-content:space-between; margin-bottom:5px;"><span>🛒 購買:</span><span id="txt-buy">--:--</span></div>
<div style="border-top:1px solid #555; padding-top:5px; text-align:center;">
<div style="font-size:10px; margin-bottom:2px;">🔊 音量: <span id="vol-disp">${Config.Settings.Volume}</span>%</div>
<input type="range" id="volume-slider-mini" min="0" max="100" value="${Config.Settings.Volume}" style="width:90%;">
</div>
</div>
`);
this.Elements.Countdown.find('#volume-slider-mini').on('input', function() {
Config.Settings.Volume = parseInt($(this).val());
$('#vol-disp').text(Config.Settings.Volume);
try { if (Game.setVolume) Game.setVolume(Config.Settings.Volume); } catch(e) {}
GM_setValue('gameVolume', Config.Settings.Volume);
});
this.makeDraggable(this.Elements.Countdown, 'countdownX', 'countdownY');
$('body').append(this.Elements.Countdown);
},
createBuffMonitor: function() {
if (this.Elements.BuffMonitor) return;
this.Elements.BuffMonitor = $(`
<div id="cookie-buff-monitor" style="
position: fixed; left: ${Config.Memory.BuffX}px; top: ${Config.Memory.BuffY}px; padding: 10px; background: rgba(0,0,0,0.85);
color: white; border-radius: 12px; font-family: 'Microsoft YaHei', sans-serif; z-index: 999996;
display: ${Config.Flags.ShowBuffMonitor ? 'block' : 'none'}; cursor: move; width: 320px;
border: 1px solid rgba(255,255,255,0.2); backdrop-filter: blur(4px);
">
<div style="font-weight: bold; margin-bottom: 10px; border-bottom: 2px solid rgba(255,255,255,0.2); padding-bottom: 8px; text-align: center; color: #ffd700;">🔥 Buff 監控</div>
<div id="buff-list-content" style="display: flex; flex-direction: column; gap: 8px;"></div>
</div>
`);
this.makeDraggable(this.Elements.BuffMonitor, 'buffX', 'buffY');
$('body').append(this.Elements.BuffMonitor);
},
updateBuffDisplay: function() {
if (!Config.Flags.ShowBuffMonitor || !this.Elements.BuffMonitor) return;
const buffList = $('#buff-list-content');
buffList.empty();
let totalCpsMult = 1;
let totalClickMult = 1;
let hasBuff = false;
if (Game.buffs) {
for (let i in Game.buffs) {
hasBuff = true;
const buff = Game.buffs[i];
if (buff.multCpS > 0) totalCpsMult *= buff.multCpS;
if (buff.multClick > 0) totalClickMult *= buff.multClick;
const iconUrl = 'img/icons.png';
const iconX = buff.icon[0] * 48;
const iconY = buff.icon[1] * 48;
const isPowerful = buff.multCpS > 50 || buff.multClick > 10;
const textColor = isPowerful ? '#ff4444' : 'white';
const bgColor = isPowerful ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.2)';
const buffName = UI.cleanName(buff.dname ? buff.dname : buff.name);
let multDisplay = '';
if (buff.multCpS > 0 && buff.multCpS !== 1) multDisplay = `產量 x${Math.round(buff.multCpS*10)/10}`;
if (buff.multClick > 0 && buff.multClick !== 1) {
if(multDisplay) multDisplay += ', ';
multDisplay += `點擊 x${Math.round(buff.multClick)}`;
}
buffList.append(`
<div style="display: flex; align-items: center; padding: 6px; background: ${bgColor}; border-radius: 8px; border: 1px solid rgba(255,255,255,0.05);">
<div style="width: 48px; height: 48px; background: url(${iconUrl}) -${iconX}px -${iconY}px; margin-right: 12px; flex-shrink: 0; border-radius:4px; box-shadow: 0 0 5px rgba(0,0,0,0.5);"></div>
<div>
<div style="font-size: 14px; font-weight: bold; color: ${textColor};">${buffName}</div>
<div style="font-size: 12px; color: #ffd700;">${multDisplay}</div>
<div style="font-size: 12px; color: #ccc;">剩餘: ${Math.ceil(buff.time / 30)}s</div>
</div>
</div>
`);
}
}
if (!hasBuff) buffList.append('<div style="text-align: center; color: #666; font-size: 13px; padding: 10px;">無活性效果</div>');
let summaryHtml = '<div style="margin-top: 8px; padding-top: 8px; border-top: 1px dashed rgba(255,255,255,0.3); text-align: right;">';
let effectiveClickPower = totalCpsMult * totalClickMult;
if (totalCpsMult !== 1) {
let val = totalCpsMult < 1 ? totalCpsMult.toFixed(2) : Math.round(totalCpsMult).toLocaleString();
summaryHtml += `<div style="color: #4caf50; font-weight: bold; font-size: 14px;">🏭 總產量: x${val}</div>`;
}
if (effectiveClickPower > 1) {
let val = Math.round(effectiveClickPower).toLocaleString();
summaryHtml += `<div style="color: #ff9800; font-weight: bold; font-size: 16px; margin-top: 2px;">⚡ 點擊倍率: x${val}</div>`;
}
summaryHtml += '</div>';
buffList.append(summaryHtml);
},
makeDraggable: function(element, keyX, keyY, handleSelector = null) {
let isDragging = false, startX = 0, startY = 0;
const handle = handleSelector ? element.find(handleSelector) : element;
handle.on('mousedown', function(e) {
if ($(e.target).is('input, select, button')) return;
isDragging = true; startX = e.clientX - parseInt(element.css('left')); startY = e.clientY - parseInt(element.css('top'));
});
$(document).on('mousemove', function(e) {
if (isDragging) element.css({left: e.clientX - startX + 'px', top: e.clientY - startY + 'px'});
}).on('mouseup', function() {
if (isDragging) { isDragging = false; GM_setValue(keyX, parseInt(element.css('left'))); GM_setValue(keyY, parseInt(element.css('top'))); }
});
},
bindEvents: function() {
const self = this;
// 綁定 Checkbox -> Config.Flags -> GM_setValue
const bindChk = (id, key) => {
$('#'+id).change(function() {
Config.Flags[key] = this.checked;
GM_setValue('is'+key+(key.includes('Enabled')?'':'Enabled'), this.checked); // 兼容舊命名
// Special handlers
if(key==='Click') self.updateButtonState();
if(key==='ShowCountdown') self.Elements.Countdown.toggle(this.checked);
if(key==='ShowBuffMonitor') self.Elements.BuffMonitor.toggle(this.checked);
if(key==='GardenOverlay') Logic.Garden.clearOverlay();
});
};
bindChk('chk-auto-click', 'Click');
bindChk('chk-auto-buy', 'Buy');
bindChk('chk-auto-golden', 'Golden');
bindChk('chk-auto-garden', 'Garden');
bindChk('chk-stock', 'Stock');
bindChk('chk-se', 'SE');
bindChk('chk-spell', 'Spell');
bindChk('chk-ui-count', 'ShowCountdown');
bindChk('chk-ui-buff', 'ShowBuffMonitor');
bindChk('chk-garden-overlay', 'GardenOverlay');
bindChk('chk-garden-mutation', 'GardenMutation');
bindChk('chk-garden-smart', 'GardenAvoidBuff');
// New v8.0 bindings
bindChk('chk-season', 'Season');
bindChk('chk-santa', 'Santa');
$('#garden-save-btn').click(() => Logic.Garden.saveLayout());
$('#buy-strategy').change(function() { Config.Settings.BuyStrategy = $(this).val(); GM_setValue('buyStrategy', Config.Settings.BuyStrategy); });
$('#spd-slider').on('input', function() { Config.Settings.ClickInterval = parseInt($(this).val()); $('#spd-val').text(Config.Settings.ClickInterval); GM_setValue('clickInterval', Config.Settings.ClickInterval); });
const updateBuyInt = () => {
const min = parseInt($('#buy-min').val()); const sec = parseInt($('#buy-sec').val());
Config.Settings.BuyIntervalMs = (min * 60 + sec) * 1000;
GM_setValue('buyIntervalMinutes', min); GM_setValue('buyIntervalSeconds', sec);
};
$('#buy-min, #buy-sec').change(updateBuyInt);
const updateRstInt = () => {
const hr = parseInt($('#rst-hr').val()); const min = parseInt($('#rst-min').val());
Config.Settings.RestartIntervalMs = (hr * 3600 + min * 60) * 1000;
Core.scheduleRestart();
GM_setValue('restartIntervalHours', hr); GM_setValue('restartIntervalMinutes', min);
};
$('#rst-hr, #rst-min').change(updateRstInt);
$('#btn-force-restart').click(() => { if(confirm('確定要刷新?')) Core.performRestart(); });
}
};
// ═══════════════════════════════════════════════════════════════
// 2. 核心邏輯模組 (Business Logic)
// ═══════════════════════════════════════════════════════════════
const Logic = {
// 模組:點擊與黃金餅乾
Click: {
lastRun: 0,
update: function(now) {
// 1. 大餅乾點擊
if (Config.Flags.Click && now - this.lastRun >= Config.Settings.ClickInterval) {
const bigCookie = document.querySelector('#bigCookie');
if (bigCookie) { bigCookie.click(); Runtime.Stats.ClickCount++; }
this.lastRun = now;
}
// 2. 黃金餅乾 / 馴鹿 / 糖塊 (高頻檢測)
if (Config.Flags.Golden) {
if (Game.canLumps() && Game.lumpCurrentType !== 3 && (Date.now() - Game.lumpT) >= Game.lumpRipeAge) Game.clickLump();
// 點擊 Shimmers (金餅乾、馴鹿)
document.querySelectorAll('#shimmers > div.shimmer').forEach(c => c.click());
}
// 3. 魔法
this.handleSpells();
// 4. 彈窗確認
this.handlePrompts();
},
handleSpells: function() {
if ((!Config.Flags.Spell && !Config.Flags.SE) || !Game.Objects['Wizard tower'].minigame) return;
const M = Game.Objects['Wizard tower'].minigame;
// HoF
if (Config.Flags.Spell && M.magic >= M.getSpellCost(M.spells['hand of fate'])) {
let shouldCast = false;
for (let i in Game.buffs) {
if (Game.buffs[i].multCpS > 7 || Game.buffs[i].multClick > 10) { shouldCast = true; break; }
if (Game.buffs[i].multCpS === 7 && M.magic >= M.magicM * 0.95) { shouldCast = true; break; }
}
if (shouldCast) {
M.castSpell(M.spells['hand of fate']);
console.log('🧙♂️ [AutoSpell] 觸發連擊:命運之手');
}
}
// SE
if (Config.Flags.SE && M.magic >= M.getSpellCost(M.spells['spontaneous edifice'])) {
if (M.magic >= M.magicM * 0.95 && Object.keys(Game.buffs).length === 0 && document.querySelectorAll('.shimmer').length === 0) {
console.log('🧙♂️ [AutoSpell] 閒置期:免費召喚了一座建築 (Spontaneous Edifice)');
M.castSpell(M.spells['spontaneous edifice']);
}
}
},
handlePrompts: function() {
const yesButton = document.querySelector('#promptOption0');
if (yesButton && document.querySelector('#promptContent')) {
const txt = document.querySelector('#promptContent').textContent;
if (['Warning', 'One Mind', 'revoke', '警告', '不好的结果'].some(k => txt.includes(k))) {
console.log('⚠️ [Safe] Auto-confirming prompt:', txt);
yesButton.click();
}
}
}
},
// 模組:自動購買
Buy: {
update: function(now) {
if (!Config.Flags.Buy || now < Runtime.Timers.NextBuy) return;
// 1. 誓約
if (Game.Upgrades['Elder Pledge'] && Game.Upgrades['Elder Pledge'].unlocked && Game.Upgrades['Elder Pledge'].bought === 0 && Game.Upgrades['Elder Pledge'].canBuy()) {
console.log('🛡️ [AutoBuy] 誓約過期,強制後台購買!');
Game.Upgrades['Elder Pledge'].buy();
Runtime.Timers.NextBuy = now + Config.Settings.BuyIntervalMs;
return;
}
// 2. 科技
if (Config.Flags.Research) {
const research = document.querySelectorAll('#techUpgrades .crate.upgrade.enabled');
if (research.length > 0) {
const item = research[0];
console.log(`🛒 [購買-科技] 研究: ${this.getUpgradeName(item)}`);
item.click();
Runtime.Timers.NextBuy = now + Config.Settings.BuyIntervalMs;
return;
}
}
// 3. 升級 (含季節餅乾)
const upgradesList = document.querySelectorAll('#upgrades .crate.upgrade.enabled');
if (upgradesList.length > 0) {
const selected = this.selectByStrategy(upgradesList, true);
if (selected) {
console.log(`🛒 [購買-升級] 購買: ${this.getUpgradeName(selected)}`);
selected.click();
Runtime.Stats.BuyUpgradeCount++;
Runtime.Timers.NextBuy = now + Config.Settings.BuyIntervalMs;
return;
}
}
// 4. 建築
const buildings = document.querySelectorAll('.product.unlocked.enabled');
if (buildings.length > 0) {
const selected = this.selectByStrategy(buildings, false);
if (selected) {
const buildId = selected.id.replace('product', '');
let buildName = "未知建築";
if(Game.ObjectsById[buildId]) {
const obj = Game.ObjectsById[buildId];
const domName = document.getElementById('productName' + obj.id);
buildName = domName ? domName.innerText : (obj.displayName || obj.name);
}
console.log(`🛒 [購買-建築] 建造: ${buildName}`);
selected.click();
Runtime.Stats.BuyBuildingCount++;
Runtime.Timers.NextBuy = now + Config.Settings.BuyIntervalMs;
}
}
},
getUpgradeName: function(element) {
let name = "未知物品";
try {
const id = element.getAttribute('data-id');
if (id && Game.UpgradesById[id]) {
const obj = Game.UpgradesById[id];
name = UI.cleanName(obj.dname || obj.name);
}
} catch(e) {}
return name;
},
selectByStrategy: function(items, isUpgrade) {
let itemsArray = Array.from(items).filter(item => {
const id = item.getAttribute('data-id');
if (isUpgrade) {
if (id === '84') return false; // Chocolate egg check
// v8.0: 如果季節系統啟用,不要在此亂買切換開關,交給 Season Manager
if (Config.Flags.Season && Game.UpgradesById[id] && Game.UpgradesById[id].toggle) return false;
}
if (!isUpgrade) {
const bId = item.id.replace('product', '');
if (Game.ObjectsById[bId] && Game.ObjectsById[bId].name === 'Wizard tower' && Game.ObjectsById[bId].amount >= Config.Settings.MaxWizardTowers) return false;
}
return true;
});
if (itemsArray.length === 0) return null;
if (Config.Settings.BuyStrategy === 'random') return itemsArray[Math.floor(Math.random() * itemsArray.length)];
if (Config.Settings.BuyStrategy === 'cheapest') return itemsArray[0];
return itemsArray[itemsArray.length - 1];
}
},
// 模組:花園
Garden: {
update: function(now) {
if (!Config.Flags.Garden || now < Runtime.Timers.NextGarden) return;
if (typeof Game === 'undefined' || !Game.Objects['Farm'].minigameLoaded) return;
const M = Game.Objects['Farm'].minigame;
if (!M) return;
let isCpsBuffActive = false;
if (Config.Flags.GardenAvoidBuff) {
for (let i in Game.buffs) {
if (Game.buffs[i].multCpS > 1) { isCpsBuffActive = true; break; }
}
}
for (let y = 0; y < 6; y++) {
for (let x = 0; x < 6; x++) {
if (!M.isTileUnlocked(x, y)) continue;
const tile = M.plot[y][x];
const tileId = tile[0];
const tileAge = tile[1];
const savedId = Config.Memory.SavedGardenPlot[y][x];
// 有植物
if (tileId > 0) {
const plant = M.plantsById[tileId - 1];
const isAnomaly = (savedId !== -1 && tileId !== savedId) || (savedId === -1);
const plantName = UI.cleanName(plant.name);
// 非異變:正常收割
if (!isAnomaly) {
if (tileAge >= 98 && tileAge >= plant.mature) M.harvest(x, y);
continue;
}
// 異變處理
if (Config.Flags.GardenMutation) {
if (plant.unlocked) {
M.harvest(x, y);
console.log(`🧹 [花園] 鏟除雜物/已知變異 (紅框): ${plantName}`);
} else {
if (tileAge >= plant.mature) {
M.harvest(x, y);
console.log(`🎉 [花園] 成功收割新品種種子 (紫框): ${plantName}`);
}
}
} else {
if (plant.weed) M.harvest(x, y);
}
continue;
}
// 空地:補種
if (tileId === 0) {
if (savedId !== -1 && savedId !== null) {
const seed = M.plantsById[savedId - 1];
if (seed && seed.unlocked && M.canPlant(seed)) {
if (Config.Flags.GardenAvoidBuff && isCpsBuffActive) continue;
M.useTool(seed.id, x, y);
}
}
}
}
}
Runtime.Timers.NextGarden = now + 2500;
},
updateOverlay: function() {
if (!Config.Flags.GardenOverlay) return;
if (typeof Game === 'undefined' || !Game.Objects['Farm'].minigameLoaded) return;
const M = Game.Objects['Farm'].minigame;
if (!M) return;
for (let y = 0; y < 6; y++) {
for (let x = 0; x < 6; x++) {
const tileDiv = document.getElementById(`gardenTile-${x}-${y}`);
if (!tileDiv) continue;
tileDiv.classList.remove('cc-overlay-missing', 'cc-overlay-anomaly', 'cc-overlay-correct', 'cc-overlay-new');
if (!M.isTileUnlocked(x, y)) continue;
const savedId = Config.Memory.SavedGardenPlot[y][x];
const realId = M.plot[y][x][0];
if (realId === 0 && savedId !== -1) {
tileDiv.classList.add('cc-overlay-missing');
} else if (realId !== 0) {
const plant = M.plantsById[realId - 1];
const isAnomaly = (savedId !== -1 && realId !== savedId) || (savedId === -1);
if (isAnomaly) {
if (plant.unlocked) tileDiv.classList.add('cc-overlay-anomaly');
else tileDiv.classList.add('cc-overlay-new');
} else if (realId === savedId) {
tileDiv.classList.add('cc-overlay-correct');
}
}
}
}
},
clearOverlay: function() {
$('.cc-overlay-missing, .cc-overlay-anomaly, .cc-overlay-correct, .cc-overlay-new').removeClass('cc-overlay-missing cc-overlay-anomaly cc-overlay-correct cc-overlay-new');
},
saveLayout: function() {
if (typeof Game === 'undefined' || !Game.Objects['Farm'].minigame) { alert('花園未就緒!'); return; }
const M = Game.Objects['Farm'].minigame;
let newLayout = [];
for (let y = 0; y < 6; y++) {
let row = [];
for (let x = 0; x < 6; x++) {
if (M.isTileUnlocked(x, y)) {
const tile = M.plot[y][x];
row.push(tile[0] === 0 ? -1 : tile[0]);
} else {
row.push(-1);
}
}
newLayout.push(row);
}
Config.Memory.SavedGardenPlot = newLayout;
GM_setValue('savedGardenPlot', Config.Memory.SavedGardenPlot);
const btn = $('#garden-save-btn');
const originalText = btn.text();
btn.text('✅ 已儲存!').css('background', '#4caf50');
setTimeout(() => btn.text(originalText).css('background', '#2196f3'), 1500);
}
},
// 模組:股市
Stock: {
update: function(now) {
if (!Config.Flags.Stock || now < Runtime.Timers.NextStock) return;
const Bank = Game.Objects['Bank'];
if (!Bank || !Bank.minigameLoaded || !Bank.minigame) return;
const M = Bank.minigame;
for (let i = 0; i < M.goodsById.length; i++) {
const good = M.goodsById[i];
const price = M.getGoodPrice(good);
const rv = M.getRestingVal(good.id);
const goodName = UI.cleanName(good.name);
if (price < rv * 0.5) {
const maxStock = M.getGoodMaxStock(good);
if (good.stock < maxStock && Game.cookies > price) {
M.buyGood(good.id, 10000);
}
}
if (price > rv * 1.5) {
if (good.stock > 0) {
M.sellGood(good.id, 10000);
console.log(`📉 [股市] 獲利賣出 ${goodName} @ $${price.toFixed(2)} (RV: ${rv.toFixed(2)})`);
}
}
}
Runtime.Timers.NextStock = now + 3000;
}
},
// 模組:季節經理 (v8.0 NEW)
Season: {
update: function(now) {
if (!Config.Flags.Season || now < Runtime.Timers.NextSeasonCheck) return;
const currentStage = Runtime.SeasonState.Roadmap[Runtime.SeasonState.CurrentStage];
// 如果已經跑完所有 Roadmap,預設邏輯 (保持現狀 或 留在 Christmas)
if (!currentStage) return;
const currentSeason = Game.season;
const targetSeasonId = currentStage.id;
// 1. 確保在正確的季節
if (currentSeason !== targetSeasonId) {
const switcherName = targetSeasonId === 'fools' ? 'Lovesick biscuit' : 'Festive biscuit'; // 簡化邏輯,需擴充完整名稱
// 尋找開關
const switcher = Object.values(Game.Upgrades).find(u => u.toggle && u.season === targetSeasonId);
if (switcher) {
if (!switcher.bought && switcher.canBuy()) {
console.log(`🍂 [Season] 切換季節至: ${currentStage.name}`);
switcher.buy();
} else if (!switcher.unlocked) {
// 還沒解鎖開關? 通常發生在剛轉生,需要等待或手動購買前置
// 這裡不做動作,等待解鎖
}
}
Runtime.Timers.NextSeasonCheck = now + 2000;
return;
}
// 2. 執行季節目標
let isComplete = false;
if (currentStage.target === 'BuyAllUpgrades') {
// 檢查商店是否還有該季節的升級
const remaining = Object.values(Game.Upgrades).filter(u => u.season === targetSeasonId && !u.bought && u.unlocked);
if (remaining.length === 0) isComplete = true;
else {
// 嘗試購買
remaining.forEach(u => { if (u.canBuy()) { u.buy(); console.log(`🍂 [Season] 購買季節餅乾: ${u.name}`); } });
}
} else if (currentStage.target === 'MaxSanta') {
// 聖誕老人邏輯委派給 Logic.Santa,這裡只檢查結果
if (Game.santaLevel >= 14) { // 14 or 15
// 也要買完餅乾
const remaining = Object.values(Game.Upgrades).filter(u => u.season === targetSeasonId && !u.bought && u.unlocked);
if (remaining.length === 0) isComplete = true;
}
}
// 3. 切換下一階段
if (isComplete) {
console.log(`🍂 [Season] 階段完成: ${currentStage.name}`);
Runtime.SeasonState.CurrentStage++;
}
Runtime.Timers.NextSeasonCheck = now + 5000;
}
},
// 模組:聖誕老人 (v8.0 NEW)
Santa: {
update: function(now) {
if (!Config.Flags.Santa || Game.season !== 'christmas') return;
// 解鎖聖誕老人
if (Game.Has('A festive hat') && !Game.Has('Santa Claus')) {
// 需要購買帽子嗎?通常由 Buy 模組處理,這裡假設已擁有
}
// 進化邏輯
// 檢查是否有進化按鈕可用
if (Game.santaLevel < 14) { // 0-14 級
const price = Math.pow(Game.santaLevel + 1, Game.santaLevel + 1); // 簡化公式,實際上遊戲有內部定義 Game.santaDrops
// 實際上最穩的方法是模擬點擊
// 由於聖誕老人的 UI 是特殊的,我們直接調用內部函數或查找 DOM
// DOM 方式: 需要開啟 Santa 面板
// 為求穩定,這裡使用內部函數 Game.UpgradeSanta() 如果可行的話,
// 但通常需要點擊 #santa 元素來打開面板,然後點擊 #santaUpgrade
const santaEl = document.getElementById('santa');
if (santaEl && santaEl.style.display !== 'none') {
// 嘗試升級
// 這裡使用比較 hack 的方式:模擬使用者操作
// 由於這部分邏輯較複雜且依賴 UI 狀態,我們採用保守策略:
// 如果有足夠餅乾,且有 .santa-upgrade 按鈕 (假設面板已開),點它
// 如果面板沒開,點 santa
// 簡單版:只在錢很多時嘗試
// Game.UpgradeSanta() 是內部函數
if (typeof Game.UpgradeSanta === 'function') {
Game.UpgradeSanta(); // 嘗試進化
// 這會扣錢並升級,如果錢不夠它自己會擋
}
}
}
}
},
// AHK 標題更新
updateTitle: function() {
if (typeof Game === 'undefined') return;
let totalMult = 1;
let isWorthClicking = false;
if (Game.buffs) {
for (let i in Game.buffs) {
const buff = Game.buffs[i];
if (buff.multCpS > 0) totalMult *= buff.multCpS;
if (buff.multClick > 0) totalMult *= buff.multClick;
if (buff.multClick > 1 || buff.multCpS > 7) isWorthClicking = true;
}
}
let coords = "0,0";
const bigCookie = document.querySelector('#bigCookie');
if (bigCookie) {
const rect = bigCookie.getBoundingClientRect();
coords = `${Math.round(rect.left + rect.width / 2)},${Math.round(rect.top + rect.height / 2)}`;
}
const signal = isWorthClicking ? "⚡ATTACK" : "💤IDLE";
const displayMult = totalMult > 1000 ? (totalMult/1000).toFixed(1) + 'k' : Math.round(totalMult);
document.title = `[${signal}|${displayMult}x|${coords}] ${Runtime.OriginalTitle}`;
}
};
// ═══════════════════════════════════════════════════════════════
// 3. 系統核心 (System Core)
// ═══════════════════════════════════════════════════════════════
const Core = {
init: function() {
console.log('🍪 Cookie Clicker Ultimate v8.0 (Reborn) Loaded');
const scriptRestarted = localStorage.getItem('cookieScriptRestarted');
if (scriptRestarted) {
console.log('🔄 Script restarted automatically.');
localStorage.removeItem('cookieScriptRestarted');
}
UI.initStyles();
UI.createFloatingButton();
UI.createControlPanel();
UI.createCountdown();
UI.createBuffMonitor();
try { if (Game.setVolume) Game.setVolume(Config.Settings.Volume); } catch(e) {}
this.scheduleRestart();
this.startHeartbeat();
// 延遲啟動自動化,等待遊戲加載
setTimeout(() => {
Logic.Garden.clearOverlay();
UI.updateButtonState();
}, 3000);
// 快捷鍵
document.addEventListener('keydown', function(e) {
if (e.key === 'F8') {
e.preventDefault();
Config.Flags.Click = !Config.Flags.Click;
GM_setValue('isClickEnabled', Config.Flags.Click);
UI.updateButtonState();
// 更新 Checkbox
if(UI.Elements.Panel) $('#chk-auto-click').prop('checked', Config.Flags.Click);
}
});
},
// v8.0 新核心:單一循環
startHeartbeat: function() {
const self = this;
// 1. Fast Tick (UI, Click, Title) - 每 50ms (或依設定)
// 使用 setTimeout 遞迴以允許動態調整速度
const fastLoop = () => {
const now = Date.now();
Logic.Click.update(now);
setTimeout(fastLoop, Math.max(20, Config.Settings.ClickInterval)); // 限制最快 20ms
};
fastLoop();
// 2. Slow Tick (Logic, Buy, Garden, Season) - 每 1000ms
setInterval(() => {
const now = Date.now();
Logic.Buy.update(now);
Logic.Garden.update(now);
Logic.Garden.updateOverlay();
Logic.Stock.update(now);
Logic.Season.update(now);
Logic.Santa.update(now);
Logic.updateTitle();
UI.updateBuffDisplay();
// Countdown text update
if (Config.Flags.ShowCountdown) {
$('#txt-rst').text(UI.formatMs(Math.max(0, Runtime.Timers.NextRestart - now)));
$('#txt-buy').text(Config.Flags.Buy ? UI.formatMs(Math.max(0, Runtime.Timers.NextBuy - now)) : '--:--');
}
}, 1000);
},
scheduleRestart: function() {
if (Runtime.Timers.RestartInterval) clearInterval(Runtime.Timers.RestartInterval);
let interval = Config.Settings.RestartIntervalMs;
if (interval < 60000) interval = 60000;
Runtime.Timers.NextRestart = Date.now() + interval;
// 使用 setTimeout 做一次性觸發
if(this.restartTimer) clearTimeout(this.restartTimer);
this.restartTimer = setTimeout(() => this.performRestart(), interval);
},
performRestart: function() {
if (Game.WriteSave) Game.WriteSave();
localStorage.setItem('cookieScriptRestarted', 'true');
setTimeout(() => {
GM_openInTab(window.location.href, { active: true, insert: true, setParent: false });
setTimeout(() => window.close(), 1000);
}, 500);
}
};
// 啟動腳本
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => setTimeout(() => Core.init(), 1500));
} else {
setTimeout(() => Core.init(), 1500);
}
})();