Automated clicker, auto-buy, auto-harvest, garden manager, stock market, season manager, and Santa evolver.
当前为
// ==UserScript==
// @name Cookie Clicker Ultimate Automation
// @name:zh-TW 餅乾點點樂全自動掛機輔助 (Cookie Clicker)
// @name:zh-CN 饼干点点乐全自动挂机辅助 (Cookie Clicker)
// @namespace http://tampermonkey.net/
// @version 8.0.6
// @description Automated clicker, auto-buy, auto-harvest, garden manager, stock market, season manager, and Santa evolver.
// @description:zh-TW 全功能自動掛機腳本 v8.0.6:修復長者誓約(Elder Pledge)無法重複購買導致的死結問題。
// @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', 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),
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)
},
// 參數
Settings: {
Volume: GM_getValue('gameVolume', 50),
ClickInterval: GM_getValue('clickInterval', 10), // Performance: 10ms
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,
Roadmap: [
{ name: 'Valentine', id: 'fools', target: 'BuyAllUpgrades' },
{ name: 'Christmas', id: 'christmas', target: 'MaxSanta' }
]
}
};
// ═══════════════════════════════════════════════════════════════
// 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);
},
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;
};
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 = $(`
<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.6</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.6</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;
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');
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');
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();
document.querySelectorAll('#shimmers > div.shimmer').forEach(c => c.click());
}
// 3. 魔法
this.handleSpells();
},
handleSpells: function() {
if ((!Config.Flags.Spell && !Config.Flags.SE) || !Game.Objects['Wizard tower'].minigame) return;
const M = Game.Objects['Wizard tower'].minigame;
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] 觸發連擊:命運之手');
}
}
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;
if (typeof Game === 'undefined') return;
// 1. Elder Pledge (ID 74) - 優先 (v8.0.6 Hotfix: 移除 bought===0 檢查)
const pledge = Game.Upgrades['Elder Pledge'];
if (pledge && pledge.unlocked && pledge.canBuy()) {
console.log('🛡️ [AutoBuy] 誓約過期,強制後台購買!');
pledge.buy();
Runtime.Timers.NextBuy = now + Config.Settings.BuyIntervalMs;
return;
}
// 2. Upgrades (從 Data 中讀取)
let affordable = Game.UpgradesInStore.filter(u => u.canBuy());
// 過濾器
affordable = affordable.filter(u => {
// Exclude Elder Covenant (84)
if (u.id === 84) return false;
// v8.0.6 Hotfix: Exclude Elder Pledge (74) - Handled above
if (u.id === 74) return false;
// v8.0.5 Fix: 只有當它是 toggle 且明確屬於某個季節 (u.season) 時,才留給季節經理處理
if (Config.Flags.Season && u.toggle && u.season) 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];
// v8.0.5 Localization Fix: use dname -> name
let upName = target.dname || target.name;
console.log(`🛒 [AutoBuy] Upgrade: ${UI.cleanName(upName)}`);
target.buy();
Runtime.Stats.BuyUpgradeCount++;
Runtime.Timers.NextBuy = now + Config.Settings.BuyIntervalMs;
return; // 買了升級就不買建築
}
// 3. Buildings (從 Data 中讀取)
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];
// v8.0.5 Hybrid Localization for Buildings
let buildName = targetB.displayName || targetB.name; // Fallback
const domElement = document.getElementById('productName' + targetB.id);
if (domElement) {
buildName = domElement.innerText; // Use DOM text if visible (Localized)
}
console.log(`🛒 [AutoBuy] Building: ${UI.cleanName(buildName)}`);
targetB.buy(1);
Runtime.Stats.BuyBuildingCount++;
Runtime.Timers.NextBuy = now + Config.Settings.BuyIntervalMs;
}
}
},
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;
}
},
Season: {
update: function(now) {
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()) {
console.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(); console.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) {
console.log(`🍂 [Season] 階段完成: ${currentStage.name}`);
Runtime.SeasonState.CurrentStage++;
}
Runtime.Timers.NextSeasonCheck = now + 5000;
}
},
Santa: {
update: function(now) {
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 = {
init: function() {
console.log('🍪 Cookie Clicker Ultimate v8.0.6 (Hotfix) 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();
if(UI.Elements.Panel) $('#chk-auto-click').prop('checked', Config.Flags.Click);
}
});
},
startHeartbeat: function() {
const self = this;
const fastLoop = () => {
const now = Date.now();
Logic.Click.update(now);
setTimeout(fastLoop, Math.max(10, Config.Settings.ClickInterval)); // Min 10ms
};
fastLoop();
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();
Logic.Click.handlePrompts();
UI.updateBuffDisplay();
if (Config.Flags.ShowCountdown) {
$('#txt-rst').text(UI.formatMs(Math.max(0, Runtime.Timers.NextRestart - now)));
$('#txt-buy').text(Config.Flags.Buy ? UI.formatMs(Math.max(0, Runtime.Timers.NextBuy - now)) : '--:--');
}
}, 1000);
},
scheduleRestart: function() {
if (Runtime.Timers.RestartInterval) clearInterval(Runtime.Timers.RestartInterval);
let interval = Config.Settings.RestartIntervalMs;
if (interval < 60000) interval = 60000;
Runtime.Timers.NextRestart = Date.now() + interval;
if(this.restartTimer) clearTimeout(this.restartTimer);
this.restartTimer = setTimeout(() => this.performRestart(), interval);
},
performRestart: function() {
if (Game.WriteSave) Game.WriteSave();
localStorage.setItem('cookieScriptRestarted', 'true');
setTimeout(() => {
GM_openInTab(window.location.href, { active: true, insert: true, setParent: false });
setTimeout(() => window.close(), 1000);
}, 500);
}
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => setTimeout(() => Core.init(), 1500));
} else {
setTimeout(() => Core.init(), 1500);
}
})();