Automated clicker, auto-buy, auto-harvest, garden manager, stock market, season manager, Santa evolver, and Smart Sugar Lump harvester.
当前为
// ==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
// ==/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 = $(`
<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.5.0</span>
</div>
<!-- ✅ v8.5.0 新增:全局狀態列 -->
<div id="global-status-bar" style="
padding: 10px 15px;
background: #4caf50;
color: white;
font-weight: bold;
font-size: 14px;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
transition: background 0.3s;
">
<span id="status-icon">🟢</span>
<span id="status-text">系統運行中</span>
<button id="btn-toggle-master" style="
padding: 4px 12px;
background: rgba(255, 255, 255, 0.2);
color: white;
border: 1px solid rgba(255, 255, 255, 0.5);
border-radius: 4px;
cursor: pointer;
font-size: 12px;
font-weight: bold;
">暫停 (F8)</button>
</div>
<!-- ✅ v8.5.0 優化:增加底部 Padding 防止內容被遮擋 -->
<div style="padding: 20px; padding-bottom: 50px; overflow-y: auto; max-height: calc(85vh - 110px);">
<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>
<label><input type="checkbox" id="chk-research" ${Config.Flags.Research?'checked':''}> 🔬 自動科技研發</label>
<label><input type="checkbox" id="chk-wrinkler" ${Config.Flags.AutoWrinkler?'checked':''}> 🐛 自動戳皺紋蟲</label>
</div>
<div id="lump-status" style="
margin-top: 10px; padding: 6px; background: rgba(0,0,0,0.05); border-radius: 4px;
font-size: 12px; font-family: monospace; color: #666; border-left: 3px solid #ccc;
">🍬 糖塊監控:初始化中...</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:#1565c0;">
<input type="checkbox" id="chk-show-garden-protection" ${Config.Flags.ShowGardenProtection?'checked':''}> 🖥️ 於花園顯示保護介面
</label>
<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>
<!-- ✅ v8.5.0 新增:日誌面板開關 -->
<label style="display:block; font-size:13px; color:#333; font-weight: bold; margin-top: 5px;">
<input type="checkbox" id="chk-ui-log" checked> 📜 操作日誌面板
</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);
},
// ✅ 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 = $(`
<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.5.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>`;
}
if (typeof Game.computedMouseCps !== 'undefined') {
let clickVal = Game.computedMouseCps;
let formattedClick = typeof Beautify !== 'undefined' ? Beautify(clickVal) : Math.round(clickVal).toLocaleString();
summaryHtml += `<div style="margin-top:5px; padding-top:5px; border-top:1px solid rgba(255,255,255,0.1); color:#fff; font-size:15px; font-family:monospace;">👆 點擊力: <span style="color: #ffd700;">+${formattedClick}</span></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 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 = $(`
<div id="action-log-panel" style="
position: fixed;
left: ${Config.Memory.ActionLogX}px;
top: ${Config.Memory.ActionLogY}px;
width: 400px;
max-height: 300px;
background: rgba(0, 0, 0, 0.95);
color: white;
border: 2px solid #2196f3;
border-radius: 8px;
padding: 10px;
z-index: 999995;
font-family: 'Consolas', 'Monaco', monospace;
font-size: 12px;
box-shadow: 0 4px 20px rgba(33, 150, 243, 0.5);
cursor: move;
display: none;
">
<div style="
font-weight: bold;
font-size: 14px;
margin-bottom: 8px;
text-align: center;
border-bottom: 1px solid #2196f3;
padding-bottom: 6px;
display: flex;
justify-content: space-between;
align-items: center;
">
<span>📜 操作日誌</span>
<span id="log-counter" style="font-size: 11px; color: #64b5f6;">0 條</span>
</div>
<div id="log-list" style="
max-height: 230px;
overflow-y: auto;
overflow-x: hidden;
padding-right: 5px;
">
<div style="text-align: center; color: #999; padding: 20px;">
尚無日誌
</div>
</div>
<div style="
margin-top: 8px;
padding-top: 6px;
border-top: 1px solid #444;
display: flex;
gap: 5px;
">
<button id="btn-clear-log" style="
flex: 1;
padding: 4px;
background: #f44336;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 11px;
">🗑️ 清空</button>
<button id="btn-close-log" style="
flex: 1;
padding: 4px;
background: #757575;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 11px;
">❌ 關閉</button>
</div>
</div>
`);
$('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 = $(`
<div style="
padding: 6px;
margin-bottom: 4px;
background: rgba(255, 255, 255, 0.05);
border-left: 3px solid ${color};
border-radius: 4px;
font-size: 11px;
line-height: 1.4;
word-break: break-word;
">
<span style="color: #999;">${timestamp}</span>
<span style="color: ${color}; margin: 0 4px;">${icon}</span>
<span style="color: #fff;">${UI.cleanName(message)}</span>
</div>
`);
// 移除「尚無日誌」提示
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(`
<div style="text-align: center; color: #999; padding: 20px;">
尚無日誌
</div>
`);
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 = $(`
<div id="garden-protection-ui" style="
position: absolute;
left: ${Config.Memory.GardenProtectionX}px;
top: ${Config.Memory.GardenProtectionY}px;
width: 240px;
background: rgba(0, 0, 0, 0.9);
color: white;
border: 2px solid #81c784;
border-radius: 8px;
padding: 12px;
z-index: 10000;
font-family: Arial, sans-serif;
box-shadow: 0 4px 20px rgba(129, 199, 132, 0.5);
cursor: move;
display: none;
">
<div style="
font-weight: bold;
font-size: 14px;
margin-bottom: 10px;
text-align: center;
border-bottom: 1px solid #81c784;
padding-bottom: 8px;
">
🛡️ 花園保護模式
</div>
<label id="spending-lock-label" style="
display: flex;
align-items: center;
font-size: 13px;
cursor: pointer;
padding: 8px;
background: rgba(255, 255, 255, 0.1);
border-radius: 4px;
transition: background 0.3s;
">
<input type="checkbox" id="chk-spending-lock" style="margin-right: 8px; width: 16px; height: 16px;">
<span style="flex: 1;">🔒 立刻停止支出</span>
</label>
<!-- ✅ v8.4.7 新增:記憶陣型按鈕 -->
<button id="btn-save-garden-layout" style="
width: 100%;
padding: 8px;
margin-top: 10px;
background: #81c784;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 13px;
font-weight: bold;
transition: background 0.3s;
">📷 記憶當前陣型</button>
<div style="
margin-top: 10px;
font-size: 11px;
color: #ffcccc;
text-align: center;
line-height: 1.4;
">
勾選後將鎖定:<br>
購買 | 花園 | 科技 | 股市
</div>
</div>
`);
$(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 = $(`
<div id="spending-lock-warning" style="
background: linear-gradient(135deg, #ff4444 0%, #cc0000 100%);
color: white;
padding: 12px;
text-align: center;
font-weight: bold;
font-size: 14px;
border-bottom: 2px solid rgba(255,255,255,0.3);
animation: pulse 2s infinite;
">
🔒 支出已鎖定 | 花園保護模式啟用中
</div>
<style>
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
</style>
`);
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 class="name">(.+?)<\/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());
}
})();