// ==UserScript==
// @name Cookie Clicker Ultimate Automation
// @name:zh-TW 餅乾點點樂全自動掛機輔助 (Cookie Clicker)
// @name:zh-CN 饼干点点乐全自动挂机辅助 (Cookie Clicker)
// @namespace http://tampermonkey.net/
// @version 8.8.5.2
// @description Automated clicker, auto-buy, auto-harvest, garden manager (5 slots), stock market, season manager, Santa evolver, Smart Sugar Lump harvester, Dragon Aura management, and the new Gambler feature.
// @description:zh-TW 全功能自動掛機腳本 v8.8.5.2 Hotfix2:Logic.Season 季節邏輯重構
// @author You & AI Architect
// @match https://wws.justnainai.com/*
// @match https://orteil.dashnet.org/cookieclicker/*
// @icon https://orteil.dashnet.org/cookieclicker/img/favicon.ico
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_openInTab
// @require https://code.jquery.com/jquery-3.6.0.min.js
// @license MIT
// @downloadURL none
// ==/UserScript==
/*
變更日誌 (Changelog):
v8.8.5.2 Hotfix 2 (2025):
- [Logic] Logic.Season.update 完全重構:採用「盤點優先、智慧跳過、零延遲」策略,優先計算庫存並在目標達成時立即跳至下一階段,修復季節切換卡死與順序邏輯問題。
v8.8.5.1 Hotfix (2025):
- [Fix] 修正 Godzamok 戰術核彈設定 (買回數量/熱鍵) 的儲存與讀取邏輯。
- [UI] 調整戰術核彈按鈕位置 (Top 0px -> 10px) 以避免遮擋。
v8.8.5 (2025):
- [Feature] 新增「戰術核彈 (Tactical Nuke)」模組:手動觸發 Godzamok 全賣 -> 買回戰術。
- [UI] 建築大師欄新增戰術核彈按鈕 (☢️)。
*/
(function() {
'use strict';
// ═══════════════════════════════════════════════════════════════
// 0. 全域配置與狀態 (Configuration & State)
// ═══════════════════════════════════════════════════════════════
const Config = {
// 開關狀態
Flags: {
GlobalMasterSwitch: GM_getValue('isGlobalMasterSwitchEnabled', true),
Click: GM_getValue('isClickEnabled', true),
Buy: GM_getValue('isBuyEnabled', true),
Golden: GM_getValue('isGoldenEnabled', true),
Spell: GM_getValue('isSpellEnabled', true),
Garden: GM_getValue('isGardenEnabled', true),
Research: GM_getValue('isResearchEnabled', true),
AutoWrinkler: GM_getValue('isAutoWrinklerEnabled', true),
Stock: GM_getValue('isStockEnabled', true),
SE: GM_getValue('isSEEnabled', true),
Season: GM_getValue('isSeasonEnabled', true),
Santa: GM_getValue('isSantaEnabled', true),
DragonAura: GM_getValue('isDragonAuraEnabled', true),
GardenOverlay: GM_getValue('isGardenOverlayEnabled', true),
GardenAvoidBuff: GM_getValue('isGardenAvoidBuff', true),
GardenMutation: GM_getValue('isGardenMutationEnabled', false),
SyncPlanting: GM_getValue('isSyncPlantingEnabled', false),
SavingMode: GM_getValue('isSavingModeEnabled', false),
SavingReplant: GM_getValue('isSavingReplantEnabled', true),
AutoPledge: GM_getValue('isAutoPledgeEnabled', true),
ShowCountdown: GM_getValue('showCountdown', true),
ShowBuffMonitor: GM_getValue('showBuffMonitor', true),
ShowGardenProtection: GM_getValue('showGardenProtection', true),
SpendingLocked: GM_getValue('spendingLocked', false),
GodzamokCombo: GM_getValue('isGodzamokComboEnabled', false),
ShowGardenGrid: GM_getValue('isShowGardenGrid', false)
},
// 參數
Settings: {
Volume: GM_getValue('gameVolume', 50),
ClickInterval: GM_getValue('clickInterval', 10),
BuyStrategy: GM_getValue('buyStrategy', 'expensive'),
BuyIntervalMs: (GM_getValue('buyIntervalHours', 0) * 3600 + GM_getValue('buyIntervalMinutes', 0) * 60 + GM_getValue('buyIntervalSeconds', 10)) * 1000,
RestartIntervalMs: (GM_getValue('restartIntervalHours', 1) * 3600 + GM_getValue('restartIntervalMinutes', 0) * 60 + GM_getValue('restartIntervalSeconds', 0)) * 1000,
MaxWizardTowers: 800,
SugarLumpGoal: 100,
SpellCooldownSuccess: 60000,
SpellCooldownFail: 60000,
GardenBufferTime: 5000,
GodzamokMinMult: 1000,
GodzamokSellAmount: 100,
GodzamokTargetBuilding: 'Farm',
GodzamokCooldown: 15000,
GodzamokBuyBackTime: 11000,
GodzamokBuyback: GM_getValue('godzamokBuyback', 400),
GodzamokHotkey: GM_getValue('godzamokHotkey', '^+F9')
},
// 記憶
Memory: {
SavedGardenPlot: Array(6).fill().map(() => Array(6).fill(-1)),
GardenSavedPlots: GM_getValue('gardenSavedPlots', ['', '', '', '', '']),
GardenSelectedSlot: GM_getValue('gardenSelectedSlot', 0),
GardenSlotNames: GM_getValue('gardenSlotNames', ['Layout 1', 'Layout 2', 'Layout 3', 'Layout 4', 'Layout 5']),
PanelX: GM_getValue('panelX', window.innerWidth / 2 - 200),
PanelY: GM_getValue('panelY', 100),
BuffX: GM_getValue('buffX', window.innerWidth - 340),
BuffY: GM_getValue('buffY', 150),
CountdownX: GM_getValue('countdownX', window.innerWidth - 170),
CountdownY: GM_getValue('countdownY', 10),
ButtonX: GM_getValue('buttonX', 50),
ButtonY: GM_getValue('buttonY', 50),
GardenProtectionX: GM_getValue('gardenProtectionX', 10),
GardenProtectionY: GM_getValue('gardenProtectionY', 10),
ActionLogX: GM_getValue('actionLogX', window.innerWidth - 420),
ActionLogY: GM_getValue('actionLogY', window.innerHeight - 350),
GardenGridX: GM_getValue('gardenGridX', 100),
GardenGridY: GM_getValue('gardenGridY', 100),
LastActiveTab: GM_getValue('lastActiveTab', 'core'),
GardenLeftExpanded: GM_getValue('gardenLeftExpanded', false),
GardenRightExpanded: GM_getValue('gardenRightExpanded', false),
LogFontSize: GM_getValue('logFontSize', 12),
LogOpacity: GM_getValue('logOpacity', 0.95),
SavedSpendingStates: GM_getValue('savedSpendingStates', {
Buy: true,
Garden: true,
Research: true,
Stock: true
}),
GardenProtectionMinimized: GM_getValue('gardenProtectionMinimized', false),
LastBuffEndTime: GM_getValue('lastBuffEndTime', 0),
SavingModeExpanded: GM_getValue('savingModeExpanded', false)
}
};
// 運行時計時器與緩存
const Runtime = {
Cache: { BigCookie: null },
Timers: {
NextBuy: 0,
NextRestart: 0,
NextGarden: 0,
NextStock: 0,
NextSeasonCheck: 0,
NextGodzamokCombo: 0,
GardenWarmup: 0
},
Stats: {
ClickCount: 0,
BuyUpgradeCount: 0,
BuyBuildingCount: 0
},
GodzamokState: {
isActive: false,
soldAmount: 0,
originalBuyState: true
},
GodzamokTacticalState: {
status: 'IDLE', // Enum: 'IDLE', 'COOLDOWN'
lock: false // 防止重複觸發的鎖
},
DragonState: {
isBursting: false,
lastSwitchTime: 0,
currentPhase: 'IDLE'
},
ModuleFailCount: {},
OriginalTitle: document.title,
SeasonState: {
CurrentStage: 0,
Roadmap: [
{ id: 'valentines', name: 'Valentines', target: 'Normal' },
{ id: 'christmas', name: 'Christmas', target: 'MaxSanta' },
{ id: 'easter', name: 'Easter', target: 'Normal' },
{ id: 'halloween', name: 'Halloween', target: 'Normal' }
],
isFarming: false
},
WarmupForceShown: false
};
// ═══════════════════════════════════════════════════════════════
// Logger 模組
// ═══════════════════════════════════════════════════════════════
const Logger = {
log: function(module, message) {
const formatted = `[${module}] ${message}`;
console.log(`ℹ️ ${formatted}`);
if (UI.ActionLog && UI.ActionLog.append) UI.ActionLog.append(formatted, 'info');
},
warn: function(module, message) {
const formatted = `[${module}] ${message}`;
console.warn(`⚠️ ${formatted}`);
if (UI.ActionLog && UI.ActionLog.append) UI.ActionLog.append(formatted, 'warn');
},
error: function(module, message, error) {
const formatted = `[${module}] ${message}`;
console.error(`❌ ${formatted}`, error || '');
if (UI.ActionLog && UI.ActionLog.append) UI.ActionLog.append(formatted, 'error');
if (typeof Game !== 'undefined' && Game.Notify) {
Game.Notify('腳本錯誤', `${module}: ${message}`, [10, 6], 10);
}
},
success: function(module, message) {
const formatted = `[${module}] ${message}`;
console.log(`%c✅ ${formatted}`, 'color: #4caf50; font-weight: bold;');
if (UI.ActionLog && UI.ActionLog.append) UI.ActionLog.append(formatted, 'success');
},
critical: function(module, message) {
const formatted = `[${module}] ⚠️ CRITICAL: ${message}`;
console.error(`%c❌ ${formatted}`, 'color: #ff0000; font-weight: bold; background: #000; padding: 2px 5px;');
if (UI.ActionLog && UI.ActionLog.append) UI.ActionLog.append(formatted, 'error');
if (typeof Game !== 'undefined' && Game.Notify) {
Game.Notify('功能已停用', `${module}: ${message}`, [10, 6], 10);
}
}
};
// ═══════════════════════════════════════════════════════════════
// 1. UI 模組
// ═══════════════════════════════════════════════════════════════
const UI = {
Elements: {
Panel: null,
FloatingBtn: null,
Countdown: null,
BuffMonitor: null
},
_lastBuffSnapshot: null,
getLocalizedPlantName: function(gridId) {
if (!gridId || gridId === 0) return '';
const plantId = gridId - 1;
if (typeof Game === 'undefined' || !Game.Objects['Farm'].minigame) return '';
const plant = Game.Objects['Farm'].minigame.plantsById[plantId];
return plant ? plant.name : '';
},
updateAllLayoutSelectors: function() {
const selectors = [$('#gardenLayoutSelect'), $('#gardenLayoutSelectGrid')];
const plots = Config.Memory.GardenSavedPlots;
const names = Config.Memory.GardenSlotNames;
const selected = Config.Memory.GardenSelectedSlot;
selectors.forEach(sel => {
if(sel.length) {
sel.empty();
for(let i=0; i<5; i++) {
let isEmpty = true;
try {
if (plots[i] && plots[i] !== '[]' && plots[i] !== '') {
const parsed = JSON.parse(plots[i]);
if (parsed.length > 0) isEmpty = false;
}
} catch(e) {}
let displayText = names[i];
if(isEmpty) displayText += ' (Empty)';
const opt = $('`; return h;
};
const buyMin = Math.floor(Config.Settings.BuyIntervalMs / 60000);
const buySec = (Config.Settings.BuyIntervalMs % 60000) / 1000;
const rstHr = Math.floor(Config.Settings.RestartIntervalMs / 3600000);
const rstMin = (Config.Settings.RestartIntervalMs % 3600000) / 60000;
const savingModeDisplay = Config.Memory.SavingModeExpanded ? 'block' : 'none';
const savingModeIcon = Config.Memory.SavingModeExpanded ? '▲' : '▼';
this.Elements.Panel = $(`
🟢
系統運行中
🛒 購買策略
間隔:
`);
this.makeDraggable(this.Elements.Panel, 'panelX', 'panelY', '#panel-header');
$('body').append(this.Elements.Panel);
this.bindEvents();
this.restoreTab();
},
restoreTab: function() {
const lastTab = Config.Memory.LastActiveTab;
$(`.cc-tab-btn[data-target="${lastTab}"]`).click();
},
togglePanel: function() {
if (!this.Elements.Panel) this.createControlPanel();
this.Elements.Panel.is(':visible') ? this.Elements.Panel.fadeOut(200) : this.Elements.Panel.fadeIn(200);
},
toggleMasterSwitch: function() {
Config.Flags.GlobalMasterSwitch = !Config.Flags.GlobalMasterSwitch;
GM_setValue('isGlobalMasterSwitchEnabled', Config.Flags.GlobalMasterSwitch);
const statusBar = $('#global-status-bar');
const statusIcon = $('#status-icon');
const statusText = $('#status-text');
const btn = $('#btn-toggle-master');
if (Config.Flags.GlobalMasterSwitch) {
statusBar.css('background', '#4caf50');
statusIcon.text('🟢');
statusText.text('系統運行中');
btn.text('暫停 (F8)');
Logger.success('Core', '全局自動化已啟動');
} else {
statusBar.css('background', '#f44336');
statusIcon.text('🔴');
statusText.text('系統已暫停');
btn.text('恢復 (F8)');
Logger.warn('Core', '全局自動化已暫停');
}
this.updateButtonState();
},
createCountdown: function() {
if (this.Elements.Countdown) return;
this.Elements.Countdown = $(`
`);
this.Elements.Countdown.find('#volume-slider-mini').on('input', function() {
Config.Settings.Volume = parseInt($(this).val());
$('#vol-disp').text(Config.Settings.Volume);
try { if (Game.setVolume) Game.setVolume(Config.Settings.Volume); } catch(e) {}
GM_setValue('gameVolume', Config.Settings.Volume);
});
this.Elements.Countdown.find('#countdown-close').click(() => {
$('#chk-ui-count').prop('checked', false).trigger('change');
});
this.makeDraggable(this.Elements.Countdown, 'countdownX', 'countdownY');
$('body').append(this.Elements.Countdown);
},
createBuffMonitor: function() {
if (this.Elements.BuffMonitor) return;
this.Elements.BuffMonitor = $(`
`);
this.Elements.BuffMonitor.find('#buff-monitor-close').click(() => {
$('#chk-ui-buff').prop('checked', false).trigger('change');
});
this.makeDraggable(this.Elements.BuffMonitor, 'buffX', 'buffY');
$('body').append(this.Elements.BuffMonitor);
},
updateBuffDisplay: function() {
if (!Config.Flags.ShowBuffMonitor || !this.Elements.BuffMonitor) return;
const buffList = $('#buff-list-content');
const currentBuffKeys = [];
let totalCpsMult = 1;
let totalClickMult = 1;
let hasBuff = false;
if (Game.buffs) {
for (let i in Game.buffs) {
hasBuff = true;
const buff = Game.buffs[i];
currentBuffKeys.push(`${buff.name}_${buff.time}`);
if (buff.multCpS > 0) totalCpsMult *= buff.multCpS;
if (buff.multClick > 0) totalClickMult *= buff.multClick;
}
}
const needFullRefresh = !this._lastBuffSnapshot ||
this._lastBuffSnapshot.length !== currentBuffKeys.length ||
!this._lastBuffSnapshot.every((key, idx) => key === currentBuffKeys[idx]);
if (needFullRefresh) {
buffList.empty();
if (Game.buffs) {
for (let i in Game.buffs) {
const buff = Game.buffs[i];
const iconUrl = 'img/icons.png';
const iconX = buff.icon[0] * 48;
const iconY = buff.icon[1] * 48;
const isPowerful = buff.multCpS > 50 || buff.multClick > 10;
const textColor = isPowerful ? '#ff4444' : 'white';
const bgColor = isPowerful ? 'rgba(255, 255, 255, 0.1)' : 'rgba(0, 0, 0, 0.2)';
const buffName = UI.cleanName(buff.dname ? buff.dname : buff.name);
let multDisplay = '';
if (buff.multCpS > 0 && buff.multCpS !== 1) multDisplay = `產量 x${Math.round(buff.multCpS*10)/10}`;
if (buff.multClick > 0 && buff.multClick !== 1) {
if(multDisplay) multDisplay += ', ';
multDisplay += `點擊 x${Math.round(buff.multClick)}`;
}
buffList.append(`
${buffName}
${multDisplay}
剩餘: ${Math.ceil(buff.time / 30)}s
`);
}
}
if (!hasBuff) {
buffList.append('無活性效果
');
}
let summaryHtml = '';
let effectiveClickPower = totalCpsMult * totalClickMult;
if (totalCpsMult !== 1) {
let val = totalCpsMult < 1 ? totalCpsMult.toFixed(2) : Math.round(totalCpsMult).toLocaleString();
summaryHtml += `
🏭 總產量: x${val}
`;
}
if (effectiveClickPower > 1) {
let val = Math.round(effectiveClickPower).toLocaleString();
summaryHtml += `
⚡ 點擊倍率: x${val}
`;
}
if (typeof Game.computedMouseCps !== 'undefined') {
let clickVal = Game.computedMouseCps;
let formattedClick = typeof Beautify !== 'undefined' ? Beautify(clickVal) : Math.round(clickVal).toLocaleString();
summaryHtml += `
👆 點擊力: +${formattedClick}
`;
}
summaryHtml += '
';
buffList.append(summaryHtml);
this._lastBuffSnapshot = currentBuffKeys;
} else {
if (Game.buffs) {
let index = 0;
for (let i in Game.buffs) {
const buff = Game.buffs[i];
const timeElement = buffList.find('.buff-entry').eq(index).find('.buff-time');
if (timeElement.length) {
timeElement.text(`剩餘: ${Math.ceil(buff.time / 30)}s`);
}
index++;
}
}
}
},
makeDraggable: function(element, keyX, keyY, handleSelector = null) {
let isDragging = false, startX = 0, startY = 0;
const handle = handleSelector ? element.find(handleSelector) : element;
handle.on('mousedown', function(e) {
if ($(e.target).is('input, select, button')) return;
isDragging = true; startX = e.clientX - parseInt(element.css('left')); startY = e.clientY - parseInt(element.css('top'));
});
$(document).on('mousemove', function(e) {
if (isDragging) {
element.css({
left: (e.clientX - startX) + 'px',
top: (e.clientY - startY) + 'px'
});
}
}).on('mouseup', function() {
if (isDragging) {
isDragging = false;
GM_setValue(keyX, parseInt(element.css('left')));
GM_setValue(keyY, parseInt(element.css('top')));
}
});
},
bindEvents: function() {
const self = this;
$('.cc-tab-btn').click(function() {
const target = $(this).data('target');
$('.cc-tab-btn').removeClass('active');
$(this).addClass('active');
$('.cc-tab-pane').removeClass('active');
$('#tab-' + target).addClass('active');
Config.Memory.LastActiveTab = target;
GM_setValue('lastActiveTab', target);
});
const bindChkWithLock = (id, key) => {
$('#' + id).change(function() {
if (Config.Flags.SpendingLocked) {
this.checked = Config.Flags[key];
Logger.warn('花園保護', '支出鎖定期間無法修改此項目');
return false;
}
Config.Flags[key] = this.checked;
GM_setValue('is' + key + 'Enabled', this.checked);
if(key==='Click') self.updateButtonState();
});
};
const bindChk = (id, key) => {
$('#'+id).change(function() {
Config.Flags[key] = this.checked;
GM_setValue('is'+key+(key.includes('Enabled')?'':'Enabled'), this.checked);
if(key==='Click') self.updateButtonState();
if(key==='ShowCountdown') self.Elements.Countdown.toggle(this.checked);
if(key==='ShowBuffMonitor') self.Elements.BuffMonitor.toggle(this.checked);
if(key==='GardenOverlay') Logic.Garden.clearOverlay();
if(key==='GardenMutation') UI.GardenGrid.updateButtonState();
if(key==='DragonAura' && this.checked && Runtime.ModuleFailCount['Dragon'] >= 10) {
Runtime.ModuleFailCount['Dragon'] = 0;
Logger.success('Core', '已重置巨龍光環熔斷計數器');
}
if(key==='SavingMode') self.updateEmbeddedState();
});
};
bindChk('chk-auto-click', 'Click');
bindChkWithLock('chk-auto-buy', 'Buy');
bindChk('chk-auto-golden', 'Golden');
bindChkWithLock('chk-auto-garden', 'Garden');
bindChkWithLock('chk-research', 'Research');
bindChk('chk-wrinkler', 'AutoWrinkler');
bindChkWithLock('chk-stock', 'Stock');
bindChk('chk-se', 'SE');
bindChk('chk-spell', 'Spell');
bindChk('chk-ui-count', 'ShowCountdown');
bindChk('chk-ui-buff', 'ShowBuffMonitor');
bindChk('chk-garden-overlay', 'GardenOverlay');
bindChk('chk-garden-mutation', 'GardenMutation');
bindChk('chk-garden-smart', 'GardenAvoidBuff');
bindChk('chk-season', 'Season');
bindChk('chk-santa', 'Santa');
bindChk('chk-dragon', 'DragonAura');
$('#chk-main-sync').change(function() {
const checked = this.checked;
Config.Flags.SyncPlanting = checked;
GM_setValue('isSyncPlantingEnabled', checked);
$('#chk-garden-sync').prop('checked', checked);
if(!checked) UI.GardenGrid.updateStatus('hide');
});
bindChk('chk-saving-mode', 'SavingMode');
bindChk('chk-saving-replant', 'SavingReplant');
bindChk('chk-auto-pledge', 'AutoPledge');
$('#header-saving-mode').click(function() {
const content = $('#content-saving-mode');
const icon = $('#icon-saving-mode');
if (content.is(':visible')) {
content.slideUp(200);
icon.text('▼');
Config.Memory.SavingModeExpanded = false;
} else {
content.slideDown(200);
icon.text('▲');
Config.Memory.SavingModeExpanded = true;
}
GM_setValue('savingModeExpanded', Config.Memory.SavingModeExpanded);
});
$('#chk-finance-lock').change(function() {
UI.GardenProtection.toggle(this.checked);
UI.GardenProtection.updateEmbeddedState();
$('#chk-spending-lock').prop('checked', this.checked);
self.updateEmbeddedState();
});
$('#chk-ui-log').change(function() { UI.ActionLog.toggle(this.checked); });
$('#btn-toggle-master').click(function() { UI.toggleMasterSwitch(); });
$('#btn-toggle-master').hover(
function() { $(this).css('background', 'rgba(255, 255, 255, 0.3)'); },
function() { $(this).css('background', 'rgba(255, 255, 255, 0.2)'); }
);
$('#chk-show-garden-protection').change(function() {
Config.Flags.ShowGardenProtection = this.checked;
GM_setValue('showGardenProtection', this.checked);
UI.GardenProtection.updateVisibility();
});
$('#btn-reset-ui-pos').click(() => {
Config.Memory.GardenProtectionX = 100;
Config.Memory.GardenProtectionY = 100;
Config.Memory.GardenGridX = 100;
Config.Memory.GardenGridY = 100;
GM_setValue('gardenProtectionX', 100);
GM_setValue('gardenProtectionY', 100);
GM_setValue('gardenGridX', 100);
GM_setValue('gardenGridY', 100);
$('#garden-protection-ui').css({left: '100px', top: '100px'});
$('#garden-grid-panel').css({left: '100px', top: '100px'});
Logger.success('UI', '所有懸浮面板位置已重置');
});
$('#garden-save-btn').click(() => Logic.Garden.saveLayout());
$('#main-panel-close').click(() => self.togglePanel());
$('#chk-godzamok').change(function() { Config.Flags.GodzamokCombo = this.checked; GM_setValue('isGodzamokComboEnabled', this.checked); });
$('#val-godzamok-amount').change(function() { Config.Settings.GodzamokSellAmount = parseInt(this.value); });
$('#val-godzamok-target').change(function() { Config.Settings.GodzamokTargetBuilding = this.value; });
$('#val-godzamok-min').change(function() { Config.Settings.GodzamokMinMult = parseInt(this.value); });
// [Fix 2] 重寫買回數量與熱鍵的事件監聽邏輯
$('#val-godzamok-buyback').change(function() {
var raw = parseInt(this.value, 10);
// 防呆:若非數字或小於-1,回退預設值
if (isNaN(raw) || raw < -1) raw = 400;
Config.Settings.GodzamokBuyback = raw;
this.value = raw; // 更新 UI 顯示標準化後的數值
GM_setValue('godzamokBuyback', raw); // 持久化
});
$('#val-godzamok-hotkey').change(function() {
var val = this.value.trim();
if (!val) return; // 防空
Config.Settings.GodzamokHotkey = val;
GM_setValue('godzamokHotkey', val); // 持久化
});
$('#btn-show-grid').click(() => UI.GardenGrid.toggle());
$('#buy-strategy').change(function() { Config.Settings.BuyStrategy = $(this).val(); GM_setValue('buyStrategy', Config.Settings.BuyStrategy); });
$('#spd-slider').on('input', function() { Config.Settings.ClickInterval = parseInt($(this).val()); $('#spd-val').text(Config.Settings.ClickInterval); GM_setValue('clickInterval', Config.Settings.ClickInterval); });
const updateBuyInt = () => {
const min = parseInt($('#buy-min').val()); const sec = parseInt($('#buy-sec').val());
Config.Settings.BuyIntervalMs = (min * 60 + sec) * 1000;
GM_setValue('buyIntervalMinutes', min); GM_setValue('buyIntervalSeconds', sec);
};
$('#buy-min, #buy-sec').change(updateBuyInt);
const updateRstInt = () => {
const hr = parseInt($('#rst-hr').val()); const min = parseInt($('#rst-min').val());
Config.Settings.RestartIntervalMs = (hr * 3600 + min * 60) * 1000;
Core.scheduleRestart();
GM_setValue('restartIntervalHours', hr); GM_setValue('restartIntervalMinutes', min);
};
$('#rst-hr, #rst-min').change(updateRstInt);
$('#btn-force-restart').click(() => { if(confirm('確定要刷新?')) Core.performRestart(); });
}
};
// ═══════════════════════════════════════════════════════════════
// 可視化操作日誌面板
// ═══════════════════════════════════════════════════════════════
UI.ActionLog = {
Elements: {
Container: null,
LogList: null,
Counter: null
},
MaxEntries: 100,
lastMsgContent: null,
lastMsgCount: 1,
lastMsgElement: null,
create: function() {
if (this.Elements.Container) return;
const fontSize = Config.Memory.LogFontSize;
const opacity = Config.Memory.LogOpacity;
this.Elements.Container = $(`
`);
$('body').append(this.Elements.Container);
this.Elements.LogList = $('#log-list');
this.Elements.Counter = $('#log-counter');
this.bindEvents();
UI.makeDraggable(this.Elements.Container, 'actionLogX', 'actionLogY');
},
bindEvents: function() {
$('#btn-clear-log').click(() => this.clear());
$('#btn-clear-log').hover(
function() { $(this).css('background', '#d32f2f'); },
function() { $(this).css('background', '#f44336'); }
);
this.Elements.Container.find('#action-log-close').click(() => {
this.toggle(false);
});
$('#log-font-slider').on('input', function() {
const size = $(this).val();
$('#action-log-panel').css('font-size', size + 'px');
Config.Memory.LogFontSize = size;
GM_setValue('logFontSize', size);
});
$('#log-opacity-slider').on('input', function() {
const opacity = $(this).val() / 100;
$('#action-log-panel').css('background', `rgba(0, 0, 0, ${opacity})`);
Config.Memory.LogOpacity = opacity;
GM_setValue('logOpacity', opacity);
});
},
append: function(message, level = 'info') {
if (!this.Elements.LogList) return;
const colors = { info: '#2196f3', warn: '#ff9800', error: '#f44336', success: '#4caf50' };
const icons = { info: 'ℹ️', warn: '⚠️', error: '❌', success: '✅' };
const color = colors[level] || colors.info;
const icon = icons[level] || icons.info;
const cleanMsg = UI.cleanName(message);
if (this.lastMsgContent === cleanMsg && this.lastMsgElement && this.Elements.LogList.has(this.lastMsgElement).length) {
this.lastMsgCount++;
const timestamp = new Date().toLocaleTimeString('zh-TW', { hour12: false });
this.lastMsgElement.find('.log-time').text(timestamp);
let countBadge = this.lastMsgElement.find('.log-count');
if (countBadge.length === 0) {
this.lastMsgElement.append(`x${this.lastMsgCount}`);
} else {
countBadge.text(`x${this.lastMsgCount}`);
}
this.Elements.LogList.prepend(this.lastMsgElement);
return;
}
this.lastMsgContent = cleanMsg;
this.lastMsgCount = 1;
const timestamp = new Date().toLocaleTimeString('zh-TW', { hour12: false });
const entry = $(`
${timestamp}
${icon}
${cleanMsg}
`);
this.lastMsgElement = entry;
this.Elements.LogList.find('div:contains("尚無日誌")').remove();
this.Elements.LogList.prepend(entry);
const entries = this.Elements.LogList.children();
if (entries.length > this.MaxEntries) entries.last().remove();
this.Elements.Counter.text(`${entries.length} 條`);
},
clear: function() {
if (!this.Elements.LogList) return;
this.Elements.LogList.html(`尚無日誌
`);
this.Elements.Counter.text('0 條');
this.lastMsgContent = null;
this.lastMsgElement = null;
},
toggle: function(visible) {
if (!this.Elements.Container) return;
visible ? this.Elements.Container.fadeIn(200) : this.Elements.Container.fadeOut(200);
$('#chk-ui-log').prop('checked', visible);
}
};
// ═══════════════════════════════════════════════════════════════
// 花園陣型可視化
// ═══════════════════════════════════════════════════════════════
UI.GardenGrid = {
Elements: { Container: null },
create: function() {
if (this.Elements.Container) return;
this.Elements.Container = $(`
`);
$('body').append(this.Elements.Container);
this.Elements.Container.find('#garden-grid-close').click(() => this.toggle());
$('#btn-grid-manual-refresh').click(() => UI.GardenGrid.update());
$('#btn-grid-toggle-mutation').click(() => {
Config.Flags.GardenMutation = !Config.Flags.GardenMutation;
GM_setValue('isGardenMutationEnabled', Config.Flags.GardenMutation);
$('#chk-garden-mutation').prop('checked', Config.Flags.GardenMutation);
this.updateButtonState();
});
$('#chk-garden-sync').change(function() {
const checked = this.checked;
Config.Flags.SyncPlanting = checked;
GM_setValue('isSyncPlantingEnabled', checked);
$('#chk-main-sync').prop('checked', checked);
if (!checked) {
UI.GardenGrid.updateStatus('hide');
}
});
$('#gardenLayoutSelectGrid').change(function() {
const newSlot = parseInt($(this).val());
Config.Memory.GardenSelectedSlot = newSlot;
GM_setValue('gardenSelectedSlot', newSlot);
// ✅ Safety Patch: Disable Mutation & Lock Spending
Config.Flags.GardenMutation = false;
GM_setValue('isGardenMutationEnabled', false);
$('#chk-garden-mutation').prop('checked', false);
UI.GardenGrid.updateButtonState();
Logger.warn('花園保護', '切換陣型:已自動關閉突變管理');
if (!Config.Flags.SpendingLocked) {
$('#chk-spending-lock').prop('checked', true).trigger('change');
Logger.warn('花園保護', `[Grid] 切換至 Slot ${newSlot + 1},已啟用資金鎖定`);
}
Logic.Garden.loadLayout();
Logic.Garden.updateOverlay();
UI.GardenGrid.update();
UI.updateAllLayoutSelectors();
});
$('#btn-rename-layout').click(function() {
const slot = Config.Memory.GardenSelectedSlot;
const oldName = Config.Memory.GardenSlotNames[slot];
let newName = prompt('重新命名此陣型:', oldName);
if (newName === null) return;
newName = newName.trim();
if (newName === '') newName = `Layout ${slot + 1}`;
if (newName.length > 20) newName = newName.substring(0, 20);
Config.Memory.GardenSlotNames[slot] = newName;
GM_setValue('gardenSlotNames', Config.Memory.GardenSlotNames);
UI.updateAllLayoutSelectors();
Logger.success('UI', `已將 Slot ${slot+1} 重新命名為 "${newName}"`);
});
$('#cc-btn-expand-left').click(() => this.toggleSide('left'));
$('#cc-btn-expand-right').click(() => this.toggleSide('right'));
$('#btn-expand-unlocked').click(function() {
const c = $('#container-unlocked');
c.toggleClass('cc-garden-list-expanded');
$(this).text(c.hasClass('cc-garden-list-expanded') ? '▲' : '▼');
});
$('#btn-expand-locked').click(function() {
const c = $('#container-locked');
c.toggleClass('cc-garden-list-expanded');
$(this).text(c.hasClass('cc-garden-list-expanded') ? '▲' : '▼');
});
if (Config.Memory.GardenLeftExpanded) this.toggleSide('left', true);
if (Config.Memory.GardenRightExpanded) this.toggleSide('right', true);
UI.makeDraggable(this.Elements.Container, 'gardenGridX', 'gardenGridY');
this.updateButtonState();
$(document).on('mouseenter', '#garden-grid-content > div', function() {
const normalizedId = $(this).data('normalized-id');
if (typeof Game !== 'undefined' && Game.Objects['Farm'].minigame) {
const M = Game.Objects['Farm'].minigame;
let plant = null;
if (normalizedId > -1) {
plant = M.plantsById[normalizedId];
}
const savedId = $(this).data('saved-id');
if (plant || savedId > -1) {
const refPlant = plant || (savedId > -1 ? M.plantsById[savedId] : null);
if (refPlant) {
UI.Tooltip.show(this, refPlant, normalizedId, savedId);
}
}
}
}).on('mouseleave', '#garden-grid-content > div', function() {
UI.Tooltip.hide();
});
},
updateButtonState: function() {
const btn = $('#btn-grid-toggle-mutation');
if (btn.length) {
if (Config.Flags.GardenMutation) {
btn.text('🧬 突變管理: 開')
.css({ 'background': '#2e7d32', 'border-color': '#4caf50' });
} else {
btn.text('🧬 突變管理: 關')
.css({ 'background': '#c62828', 'border-color': '#ff5252' });
}
}
},
updateStatus: function(type, value) {
const barGrid = $('#sync-status-bar');
const barProt = $('#prot-sync-status');
if (!Config.Flags.SyncPlanting) {
barGrid.hide();
barProt.hide();
return;
}
let bg = '', text = '';
let show = true;
if (type === 'funds') {
bg = '#ff9800';
text = `💰 資金不足: 缺 ${(value - Game.cookies).toLocaleString()}`;
} else if (type === 'buff') {
bg = '#9c27b0';
text = '⛔ 暫停: 等待 Buff 結束';
} else if (type === 'ready') {
bg = '#4caf50';
text = '✅ 同步播種運行中';
} else {
show = false;
}
if (show) {
const style = { background: bg, display: 'block' };
barGrid.css(style).text(text);
barProt.css(style).text(text);
barProt.css('font-size', '11px');
} else {
barGrid.hide();
barProt.hide();
}
},
toggle: function() {
if (!this.Elements.Container) this.create();
if (this.Elements.Container.is(':visible')) {
this.Elements.Container.fadeOut(200);
} else {
this.update();
this.updateButtonState();
this.Elements.Container.fadeIn(200);
}
},
toggleSide: function(side, forceOpen = null) {
const drawer = $(`#drawer-${side}`);
const btn = $(`#cc-btn-expand-${side}`);
const container = this.Elements.Container;
const drawerWidth = 220;
const currentX = parseInt(container.css('left')) || Config.Memory.GardenGridX;
let newX = currentX;
const isOpen = forceOpen !== null ? forceOpen : !drawer.hasClass('open');
if (isOpen) {
if (side === 'left') {
newX = Math.max(0, currentX - drawerWidth);
container.css('left', newX + 'px');
Config.Memory.GardenGridX = newX;
GM_setValue('gardenGridX', newX);
}
drawer.addClass('open');
btn.text(side === 'left' ? '▶ 收起' : '收起 ◀');
Config.Memory[side === 'left' ? 'GardenLeftExpanded' : 'GardenRightExpanded'] = true;
GM_setValue(side === 'left' ? 'gardenLeftExpanded' : 'gardenRightExpanded', true);
} else {
if (side === 'left') {
newX = currentX + drawerWidth;
container.css('left', newX + 'px');
Config.Memory.GardenGridX = newX;
GM_setValue('gardenGridX', newX);
}
drawer.removeClass('open');
btn.text(side === 'left' ? '◀ 展開' : '展開 ▶');
Config.Memory[side === 'left' ? 'GardenLeftExpanded' : 'GardenRightExpanded'] = false;
GM_setValue(side === 'left' ? 'gardenLeftExpanded' : 'gardenRightExpanded', false);
}
},
update: function() {
$('#garden-grid-content').empty();
const plot = Config.Memory.SavedGardenPlot;
const gridContent = $('#garden-grid-content');
const unlockedList = $('#cc-garden-list-unlocked').empty();
const lockedList = $('#cc-garden-list-locked').empty();
const currentLayoutList = $('#cc-garden-current-layout').empty();
const emptyHint = $('#cc-garden-empty-hint');
let plantStats = {};
if (typeof Game !== 'undefined' && Game.Objects['Farm'].minigame) {
const M = Game.Objects['Farm'].minigame;
const totalPlants = 34;
let unlockedCount = 0;
for (let y = 0; y < 6; y++) {
for (let x = 0; x < 6; x++) {
const realTile = M.plot[y]?.[x] || [0, 0];
const gameId = realTile[0];
const normalizedId = (gameId === 0) ? -1 : gameId - 1;
const savedId = plot[y][x];
let bg = '#111';
let stateClass = '';
let text = '';
let isUnlocked = M.isTileUnlocked(x, y);
if (isUnlocked) {
if (normalizedId > -1) {
if (normalizedId === savedId) {
bg = '#4caf50';
stateClass = 'cc-dashboard-correct';
text = normalizedId;
} else {
const plant = M.plantsById[normalizedId];
if (plant && plant.unlocked) {
bg = '#f44336';
stateClass = 'cc-dashboard-weed';
} else {
bg = '#9c27b0';
stateClass = 'cc-dashboard-new';
}
text = normalizedId;
}
} else {
if (savedId > -1) {
bg = '#1565c0';
stateClass = 'cc-dashboard-missing';
text = savedId;
} else {
text = '';
}
}
} else {
bg = '#000';
text = '';
}
if (savedId > -1) {
const localName = UI.getLocalizedPlantName(savedId + 1);
if (localName) {
if (!plantStats[savedId]) plantStats[savedId] = { name: localName, count: 0 };
plantStats[savedId].count++;
}
}
gridContent.append(`
${text}
`);
}
}
for (let i = 0; i < totalPlants; i++) {
const plant = M.plantsById[i];
const localName = UI.getLocalizedPlantName(plant.id + 1);
if (plant && plant.unlocked) {
unlockedCount++;
unlockedList.append(`
[${plant.id}] ${localName}
`);
}
}
if (unlockedCount === 0) unlockedList.append('(無)');
let lockedCount = 0;
for (let i = 0; i < totalPlants; i++) {
const plant = M.plantsById[i];
const localName = UI.getLocalizedPlantName(plant.id + 1);
if (plant && !plant.unlocked) {
lockedCount++;
lockedList.append(`
[${plant.id}] ${localName}
`);
}
}
if (lockedCount === 0) lockedList.append('✓ 全解鎖!');
const sortedIds = Object.keys(plantStats).map(Number).sort((a, b) => a - b);
if (sortedIds.length > 0) {
emptyHint.hide();
currentLayoutList.show();
sortedIds.forEach(id => {
const data = plantStats[id];
currentLayoutList.append(`
[${id}] ${data.name}
x${data.count}
`);
});
} else {
currentLayoutList.hide();
emptyHint.show();
}
const progressColor = unlockedCount === totalPlants ? '#4caf50' : '#ffd700';
$('#cc-garden-progress').text(`${unlockedCount}/${totalPlants}`).css('color', progressColor);
} else {
gridContent.html('花園未加載
');
emptyHint.show();
currentLayoutList.hide();
}
}
};
// ═══════════════════════════════════════════════════════════════
// Rich Tooltip 模組
// ═══════════════════════════════════════════════════════════════
UI.Tooltip = {
Elements: { Container: null },
create: function() {
if (this.Elements.Container) return;
this.Elements.Container = $(`
`);
$('body').append(this.Elements.Container);
},
show: function(element, plant, normalizedId, savedId) {
if (!this.Elements.Container) this.create();
const M = Game.Objects['Farm'].minigame;
let displayPlant = plant;
if (!displayPlant && savedId > -1) {
displayPlant = M.plantsById[savedId];
}
if (!displayPlant) {
this.hide();
return;
}
const plantName = UI.cleanName(displayPlant.name);
let price = 0;
try {
price = M.getCost(displayPlant);
} catch(e) {
price = 0;
}
const affordable = Game.cookies >= price;
const priceText = typeof Beautify !== 'undefined' ? Beautify(price) : Math.round(price).toLocaleString();
let statusText = '';
let statusClass = '';
if (normalizedId === -1 && savedId > -1) {
statusText = '🔵 缺種子';
statusClass = 'status-missing';
} else if (normalizedId === savedId) {
statusText = '🟢 正確';
statusClass = 'status-correct';
} else if (normalizedId > -1) {
const realPlant = M.plantsById[normalizedId];
if (realPlant) {
if (realPlant.unlocked) {
statusText = `🔴 異常: ${UI.cleanName(realPlant.name)}`;
statusClass = 'status-weed';
} else {
statusText = `🟣 變異: ${UI.cleanName(realPlant.name)}`;
statusClass = 'status-new';
}
} else {
statusText = '🟣 異常';
statusClass = 'status-new';
}
} else if (savedId === -1) {
statusText = '⚫ 未設定';
statusClass = 'status-locked';
}
this.Elements.Container.empty();
const $name = $('').addClass('tooltip-name').text(plantName);
const $priceVal = $('
')
.addClass(affordable ? 'price-affordable' : 'price-unaffordable')
.text(priceText + ' 🍪');
const $priceDiv = $('').addClass('tooltip-price')
.append($('
').text('價格: '))
.append($priceVal);
const $status = $('').addClass('tooltip-status ' + statusClass).text(statusText);
this.Elements.Container.append($name).append($priceDiv).append($status);
const rect = element.getBoundingClientRect();
const tooltipWidth = this.Elements.Container.outerWidth();
const tooltipHeight = this.Elements.Container.outerHeight();
let left = rect.right + 10;
let top = rect.top;
if (left + tooltipWidth > window.innerWidth) {
left = rect.left - tooltipWidth - 10;
}
if (top + tooltipHeight > window.innerHeight) {
top = window.innerHeight - tooltipHeight - 10;
}
if (top < 10) {
top = 10;
}
this.Elements.Container.css({
left: left + 'px',
top: top + 'px',
display: 'block'
});
},
hide: function() {
if (this.Elements.Container) {
this.Elements.Container.hide();
}
}
};
// ═══════════════════════════════════════════════════════════════
// 花園保護模組
// ═══════════════════════════════════════════════════════════════
UI.GardenProtection = {
Elements: {
Container: null,
EmbeddedControls: null
},
SavedStates: { Buy: null, Garden: null, Research: null, Stock: null },
_cachedGardenPanel: null,
create: function() {
if (this.Elements.Container) return;
const Farm = Game.Objects['Farm'];
if (!Farm || !Farm.minigameLoaded) return;
const checkGardenPanel = () => {
const gardenPlot = document.getElementById('gardenPlot');
if (!gardenPlot) {
setTimeout(checkGardenPanel, 500);
return;
}
this.createProtectionUI(gardenPlot);
this.createEmbeddedControls(gardenPlot);
};
checkGardenPanel();
},
createProtectionUI: function(gardenPanel) {
this.Elements.Container = $(`
勾選後將鎖定:
購買 (誓約除外) | 花園 | 科技 | 股市
`);
$(gardenPanel).append(this.Elements.Container);
this.Elements.Container.find('#garden-prot-minimize').click(() => {
this.minimize();
});
$('#spending-lock-label').hover(
function() { $(this).css('background', 'rgba(255, 255, 255, 0.2)'); },
function() { $(this).css('background', 'rgba(255, 255, 255, 0.1)'); }
);
$('#btn-save-garden-layout').hover(
function() { $(this).css('background', '#1976d2'); },
function() { $(this).css('background', '#2196f3'); }
);
$('#btn-show-grid').hover(
function() { $(this).css('background', '#795548'); },
function() { $(this).css('background', '#8d6e63'); }
);
this.bindEvents();
UI.updateAllLayoutSelectors();
UI.makeDraggable(this.Elements.Container, 'gardenProtectionX', 'gardenProtectionY');
},
createEmbeddedControls: function(gardenPlot) {
if (this.Elements.EmbeddedControls) return;
this.Elements.EmbeddedControls = $(`
`);
$(gardenPlot).append(this.Elements.EmbeddedControls);
this.bindEmbeddedEvents();
this.updateEmbeddedState();
this.updateEmbeddedVisibility();
},
bindEmbeddedEvents: function() {
const self = this;
$('#btn-embed-restore').click(() => {
this.restore();
});
$('#btn-embed-toggle-lock').click(() => {
$('#chk-spending-lock').prop('checked', !Config.Flags.SpendingLocked).trigger('change');
});
$('#btn-embed-save').click(() => {
this.saveCurrentLayout();
});
$('#btn-embed-show').click(() => {
UI.GardenGrid.toggle();
});
},
updateEmbeddedState: function() {
const btn = $('#btn-embed-toggle-lock');
if (btn.length === 0) return;
if (Config.Flags.SpendingLocked) {
btn.html('鎖').css({
'background': '#d32f2f',
'border-color': '#ffcdd2'
}).attr('title', '目前已停止支出,點擊以恢復');
} else {
btn.html('開').css({
'background': '#388e3c',
'border-color': '#c8e6c9'
}).attr('title', '目前允許支出,點擊以鎖定');
}
},
updateEmbeddedVisibility: function() {
if (!this.Elements.EmbeddedControls) return;
if (Config.Memory.GardenProtectionMinimized) {
this.Elements.EmbeddedControls.show();
} else {
this.Elements.EmbeddedControls.hide();
}
},
bindEvents: function() {
$('#chk-spending-lock').change(function() {
UI.GardenProtection.toggle(this.checked);
UI.GardenProtection.updateEmbeddedState();
});
$('#btn-save-garden-layout').click(function() { UI.GardenProtection.saveCurrentLayout(); });
$('#btn-show-grid').click(function() { UI.GardenGrid.toggle(); });
$('#gardenLayoutSelect').change(function() {
const newSlot = parseInt($(this).val());
Config.Memory.GardenSelectedSlot = newSlot;
GM_setValue('gardenSelectedSlot', newSlot);
// ✅ Safety Patch: Disable Mutation & Lock Spending
Config.Flags.GardenMutation = false;
GM_setValue('isGardenMutationEnabled', false);
$('#chk-garden-mutation').prop('checked', false);
UI.GardenGrid.updateButtonState();
Logger.warn('花園保護', '切換陣型:已自動關閉突變管理');
if (!Config.Flags.SpendingLocked) {
$('#chk-spending-lock').prop('checked', true).trigger('change');
Logger.warn('花園保護', `[Panel] 切換至 Slot ${newSlot + 1},已啟用資金鎖定`);
}
Logic.Garden.loadLayout();
Logic.Garden.updateOverlay();
if ($('#garden-grid-panel').is(':visible')) {
UI.GardenGrid.update();
}
UI.updateAllLayoutSelectors();
});
},
minimize: function() {
if (this.Elements.Container) {
this.Elements.Container.hide();
}
if (this.Elements.EmbeddedControls) {
this.Elements.EmbeddedControls.show();
}
Config.Memory.GardenProtectionMinimized = true;
GM_setValue('gardenProtectionMinimized', true);
Logger.log('花園保護', '面板已最小化(使用右側嵌入式控制台)');
},
restore: function() {
if (this.Elements.EmbeddedControls) {
this.Elements.EmbeddedControls.hide();
}
if (this.Elements.Container && Config.Flags.ShowGardenProtection) {
this.Elements.Container.show();
}
Config.Memory.GardenProtectionMinimized = false;
GM_setValue('gardenProtectionMinimized', false);
Logger.log('花園保護', '面板已恢復');
},
toggle: function(enabled, uiOnly = false) {
if (enabled) {
if (!uiOnly) {
this.SavedStates.Buy = Config.Flags.Buy;
this.SavedStates.Garden = Config.Flags.Garden;
this.SavedStates.Research = Config.Flags.Research;
this.SavedStates.Stock = Config.Flags.Stock;
Config.Memory.SavedSpendingStates = { ...this.SavedStates };
GM_setValue('savedSpendingStates', Config.Memory.SavedSpendingStates);
GM_setValue('spendingLocked', true);
}
// ✅ Safety Patch: Disable Mutation when Locking
Config.Flags.GardenMutation = false;
GM_setValue('isGardenMutationEnabled', false);
$('#chk-garden-mutation').prop('checked', false);
UI.GardenGrid.updateButtonState();
Config.Flags.Garden = false;
Config.Flags.Research = false;
Config.Flags.Stock = false;
const buyChk = $('#chk-auto-buy');
if (this.SavedStates.Buy) {
buyChk.prop('checked', true).prop('disabled', true).css('opacity', '0.5').parent().attr('title', '資金保護中:僅允許購買誓約');
} else {
buyChk.prop('checked', false).prop('disabled', true).css('opacity', '0.5');
}
$('#chk-auto-garden').prop('checked', false).prop('disabled', true).css('opacity', '0.5');
$('#chk-research').prop('checked', false).prop('disabled', true).css('opacity', '0.5');
$('#chk-stock').prop('checked', false).prop('disabled', true).css('opacity', '0.5');
this.showLockWarning();
if (!uiOnly) Logger.log('花園保護', '已啟用支出鎖定 (允許誓約)');
} else {
Config.Flags.Buy = this.SavedStates.Buy !== null ? this.SavedStates.Buy : true;
Config.Flags.Garden = this.SavedStates.Garden !== null ? this.SavedStates.Garden : true;
Config.Flags.Research = this.SavedStates.Research !== null ? this.SavedStates.Research : true;
Config.Flags.Stock = this.SavedStates.Stock !== null ? this.SavedStates.Stock : true;
$('#chk-auto-buy').prop('checked', Config.Flags.Buy).prop('disabled', false).css('opacity', '1').parent().removeAttr('title');
$('#chk-auto-garden').prop('checked', Config.Flags.Garden).prop('disabled', false).css('opacity', '1');
$('#chk-research').prop('checked', Config.Flags.Research).prop('disabled', false).css('opacity', '1');
$('#chk-stock').prop('checked', Config.Flags.Stock).prop('disabled', false).css('opacity', '1');
this.hideLockWarning();
if (!uiOnly) {
GM_setValue('spendingLocked', false);
this.SavedStates = { Buy: null, Garden: null, Research: null, Stock: null };
Logger.log('花園保護', '已解除支出鎖定');
}
}
Config.Flags.SpendingLocked = enabled;
if ($('#chk-finance-lock').length) {
$('#chk-finance-lock').prop('checked', enabled);
}
if (UI.updateEmbeddedState) UI.updateEmbeddedState();
},
showLockWarning: function() {
const panel = $('#cookie-control-panel');
if (panel.length && !$('#spending-lock-warning').length) {
const warning = $(`
🔒 支出已鎖定 | 花園保護模式啟用中
`);
panel.prepend(warning);
}
},
hideLockWarning: function() { $('#spending-lock-warning').remove(); },
updateVisibility: function() {
if (!this.Elements.Container || !this.Elements.EmbeddedControls) return;
const gardenPanel = $('#gardenPanel');
const isGardenOpen = gardenPanel.length > 0 && gardenPanel.is(':visible');
const now = Date.now();
const warmupRemaining = Runtime.Timers.GardenWarmup - now;
if (isGardenOpen && Config.Flags.ShowGardenProtection) {
if (warmupRemaining > 0) {
const container = this.Elements.Container;
if (Config.Memory.GardenProtectionMinimized) {
Runtime.WarmupForceShown = true;
}
container.show();
container.addClass('cc-warmup-shield');
this.Elements.EmbeddedControls.hide();
const title = container.find('span').first();
const remainingSeconds = Math.ceil(warmupRemaining / 1000);
title.text(`🛡️ 暖機保護 (${remainingSeconds}s)`);
} else {
this.Elements.Container.removeClass('cc-warmup-shield');
const title = this.Elements.Container.find('span').first();
title.text('🛡️ 花園保護模式');
if (Runtime.WarmupForceShown) {
this.minimize();
Runtime.WarmupForceShown = false;
Logger.log('花園保護', '暖機結束,自動化已就緒');
} else {
if (Config.Memory.GardenProtectionMinimized) {
this.Elements.Container.hide();
this.Elements.EmbeddedControls.show();
} else {
this.Elements.Container.fadeIn(200);
this.Elements.EmbeddedControls.hide();
}
}
}
} else {
this.Elements.Container.fadeOut(200);
this.Elements.EmbeddedControls.hide();
this.Elements.Container.removeClass('cc-warmup-shield');
}
},
saveCurrentLayout: function() {
Logic.Garden.saveLayout();
}
};
// ═══════════════════════════════════════════════════════════════
// 2. 核心邏輯模組 (Business Logic)
// ═══════════════════════════════════════════════════════════════
const Logic = {
// ✅ Feature: Gambler (v8.8.3 Updated)
Gambler: {
spin: function() {
const M = Game.Objects['Wizard tower'].minigame;
if (!M) {
Logger.error('Gambler', '魔法塔小遊戲尚未解鎖');
return;
}
const spellHoF = M.spells['hand of fate'];
const spellStretch = M.spells['stretch time'];
const cost = M.getSpellCost(spellHoF) + M.getSpellCost(spellStretch);
if (M.magic < cost) {
Logger.warn('Gambler', `魔力不足! 需要 ${Math.round(cost)} (當前: ${Math.round(M.magic)})`);
this.updateLight('Red');
return;
}
Logger.log('Gambler', '🎲 命運輪盤轉動中...');
M.castSpell(spellHoF);
// Auto-click the generated Golden Cookie
if (Game.shimmers.length > 0) {
const cookie = Game.shimmers[Game.shimmers.length - 1];
cookie.pop(); // Immediate click
} else {
Logger.error('Gambler', '找不到生成的餅乾!');
this.updateLight('Red');
return;
}
// Check Buff Result Strategy
let bestBuff = null;
// Priority Check: Dragonflight > Click frenzy > Others
if (Game.buffs['Dragonflight']) bestBuff = Game.buffs['Dragonflight'];
else if (Game.buffs['Click frenzy']) bestBuff = Game.buffs['Click frenzy'];
else {
// Fallback to searching all buffs
for (let i in Game.buffs) {
const buff = Game.buffs[i];
if (buff.multCpS > 7 || buff.multClick > 2) {
bestBuff = buff;
break;
}
}
}
if (!bestBuff) {
Logger.log('Gambler', '結果: 爛牌 (無有效 Buff)');
this.updateLight('Red');
return;
}
// Determine Tier
let tier = 'Low'; // Default
if (bestBuff.name === 'Click frenzy' || bestBuff.name === 'Dragonflight') tier = 'High';
else if (bestBuff.name === 'Frenzy') tier = 'Mid';
if (tier === 'Low') {
Logger.log('Gambler', `結果: 普通 (${bestBuff.name}), 跳過 Stretch`);
this.updateLight('Red');
return;
}
// Execute Stretch Time
const oldTime = bestBuff.time;
M.castSpell(spellStretch);
// Re-fetch buff to check time (Game objects update in place usually)
let newTime = bestBuff.time;
// Double check if buff still exists
if (Game.buffs[bestBuff.name]) {
newTime = Game.buffs[bestBuff.name].time;
}
const isSuccess = newTime > oldTime;
let color = 'Red';
if (tier === 'High') {
color = isSuccess ? 'Green' : 'Yellow';
Logger.success('Gambler', `✨ Jackpot! ${bestBuff.name} (${isSuccess ? '延長成功' : '縮短'})`);
} else if (tier === 'Mid') {
color = isSuccess ? 'Yellow' : 'Red';
Logger.log('Gambler', `✨ Mid-Tier: ${bestBuff.name} (${isSuccess ? '延長成功' : '縮短'})`);
}
this.updateLight(color);
},
updateLight: function(color) {
const light = $('#gambler-traffic-light');
if (light.length === 0) return;
// Position Update Logic (v8.8.3)
const btn = $('#statsButton');
if (btn.length > 0) {
const rect = btn[0].getBoundingClientRect();
const top = rect.top + (rect.height / 2) - 30; // Center - Radius (30)
const left = rect.right + 0; // Right side
light.css({
'top': top + 'px',
'left': left + 'px',
'display': 'block'
});
} else {
// Fallback center
light.css({
'top': '50%',
'left': '50%',
'transform': 'translate(-50%, -50%)',
'display': 'block'
});
}
let hex = '#000';
if (color === 'Green') hex = '#4caf50';
else if (color === 'Yellow') hex = '#ffeb3b';
else if (color === 'Red') hex = '#f44336';
light.css({
'background': hex,
'box-shadow': `0 0 40px ${hex}`,
'opacity': '0.8'
});
// Flash animation
light.stop(true, true).fadeIn(100).fadeOut(100).fadeIn(100).fadeOut(100).fadeIn(100);
// Auto hide after 3 seconds
setTimeout(() => {
light.animate({ opacity: 0 }, 500, function() { $(this).hide(); });
}, 3000);
}
},
Magic: {
Timers: {
NextSE: 0,
NextCombo: 0
},
update: function(now) {
if (!Config.Flags.GlobalMasterSwitch) return;
const Tower = Game.Objects['Wizard tower'];
if (!Tower || !Tower.minigameLoaded) return;
const M = Tower.minigame;
if (Config.Flags.Spell) {
this.handleCombo(now, M);
}
if (Config.Flags.SE && now >= this.Timers.NextSE) {
this.handleSE(now, M);
}
},
handleCombo: function(now, M) {
if (now < this.Timers.NextCombo) return;
let shouldCast = false;
for (let i in Game.buffs) {
const buff = Game.buffs[i];
const name = buff.name.toLowerCase();
if (name === 'click frenzy' || name === 'dragonflight' || name === 'cursed finger') {
shouldCast = true;
break;
}
if (buff.multCpS > 7 || buff.multClick > 10) {
shouldCast = true;
break;
}
}
const spell = M.spells['hand of fate'];
const spellCost = M.getSpellCost(spell);
if (shouldCast && M.magic >= spellCost) {
M.castSpell(spell);
Logger.log('AutoSpell', '觸發連擊:施放 Hand of Fate');
this.Timers.NextCombo = now + 1000;
}
},
handleSE: function(now, M) {
if (Object.keys(Game.buffs).length > 0) return;
if (M.magic < M.magicM * 0.95) return;
const spell = M.spells['spontaneous edifice'];
const spellCost = M.getSpellCost(spell);
if (M.magic < spellCost) return;
const preCount = Game.BuildingsOwned;
M.castSpell(spell);
if (Game.BuildingsOwned > preCount) {
Logger.success('AutoSpell', '閒置期:免費召喚了一座建築 (SE Success)');
this.Timers.NextSE = now + Config.Settings.SpellCooldownSuccess;
} else {
Logger.warn('AutoSpell', 'SE 施法無效 (條件不符),進入靜默模式 60s');
this.Timers.NextSE = now + Config.Settings.SpellCooldownFail;
}
}
},
Click: {
handlePrompts: function() {
const yesButton = document.querySelector('#promptOption0');
if (yesButton && document.querySelector('#promptContent')) {
const txt = document.querySelector('#promptContent').textContent;
if (['Warning', 'One Mind', 'revoke', '警告', '不好的结果'].some(k => txt.includes(k))) {
Logger.log('Safe', `Auto-confirming prompt: ${txt}`);
yesButton.click();
}
}
}
},
Dragon: {
State: Runtime.DragonState,
Auras: {
RadiantAppetite: 15,
Dragonflight: 18,
BreathOfMilk: 5
},
update: function(now) {
if (!Config.Flags.GlobalMasterSwitch || !Config.Flags.DragonAura) return;
if (typeof Game.dragonLevel === 'undefined' || Game.dragonLevel < 25) return;
if (now - this.State.lastSwitchTime < 5000) return;
let currentMult = 1;
let hasClickBuff = false;
for (let i in Game.buffs) {
const b = Game.buffs[i];
if (b.multCpS > 0) currentMult *= b.multCpS;
if (b.multClick > 0) currentMult *= b.multClick;
if (b.name === 'Click frenzy' || b.name === 'Dragonflight') {
hasClickBuff = true;
}
}
const currentAura2 = Game.dragonAuras[1];
if (hasClickBuff) {
if (this.State.currentPhase !== 'BURST') {
const multDisplay = currentMult > 1000 ? (currentMult/1000).toFixed(1)+'k' : Math.round(currentMult);
Logger.log('Dragon', `🔥 爆發開始 (倍率 x${multDisplay})。切換光環:飛龍 -> 牛奶`);
this.State.currentPhase = 'BURST';
if (currentAura2 !== this.Auras.BreathOfMilk) this._setAura(1, this.Auras.BreathOfMilk);
this.State.lastSwitchTime = now;
} else {
if (currentAura2 !== this.Auras.BreathOfMilk) {
this._setAura(1, this.Auras.BreathOfMilk);
this.State.lastSwitchTime = now;
}
}
} else {
if (this.State.currentPhase !== 'IDLE') {
Logger.log('Dragon', `🎣 爆發結束。切換光環:牛奶 -> 飛龍`);
this.State.currentPhase = 'IDLE';
if (currentAura2 !== this.Auras.Dragonflight) this._setAura(1, this.Auras.Dragonflight);
this.State.lastSwitchTime = now;
} else {
if (currentAura2 !== this.Auras.Dragonflight) {
this._setAura(1, this.Auras.Dragonflight);
this.State.lastSwitchTime = now;
}
}
}
},
_setAura: function(slot, id) {
try {
if (typeof Game.setDragonAura === 'function') {
Game.setDragonAura(slot, id);
Game.recalculateGains = 1;
}
} catch(e) {
Logger.error('Dragon', '光環切換失敗', e);
}
}
},
GodzamokCombo: {
update: function(now) {
if (!Config.Flags.GlobalMasterSwitch || !Config.Flags.GodzamokCombo) return;
if (now < Runtime.Timers.NextGodzamokCombo) return;
const Temple = Game.Objects['Temple'];
if (!Temple || !Temple.minigameLoaded) return;
const M = Temple.minigame;
if (M.slot[0] !== 2) return;
let totalMult = 1;
for (let i in Game.buffs) {
totalMult *= (Game.buffs[i].multCpS * Game.buffs[i].multClick);
}
if (totalMult < Config.Settings.GodzamokMinMult) return;
const targetName = Config.Settings.GodzamokTargetBuilding;
const building = Game.Objects[targetName];
if (!building || building.amount < Config.Settings.GodzamokSellAmount) return;
this.trigger(targetName, building, now);
},
trigger: function(targetName, building, now) {
const obj = Game.Objects[targetName];
if (!obj) {
Logger.error('Godzamok', `目標建築 ${targetName} 不存在`);
return false;
}
if (![2, 3, 4].includes(obj.id)) {
Logger.error('Godzamok', `非法目標 ${targetName} (ID check failed)。僅允許 Farm, Mine, Factory。`);
return false;
}
const costToBuyBack = building.price * Config.Settings.GodzamokSellAmount * 1.5;
if (Game.cookies < costToBuyBack) {
Logger.warn('Godzamok', `觸發失敗:資金不足以買回 (需 ${costToBuyBack})`);
Runtime.Timers.NextGodzamokCombo = now + 5000;
return;
}
Logger.log('Godzamok', `觸發連擊!倍率滿足條件`);
if (!Config.Flags.SpendingLocked) {
$('#chk-spending-lock').prop('checked', true).trigger('change');
}
building.sell(Config.Settings.GodzamokSellAmount);
Runtime.GodzamokState.soldAmount = Config.Settings.GodzamokSellAmount;
Runtime.GodzamokState.isActive = true;
setTimeout(() => {
this.buyBack(targetName);
}, Config.Settings.GodzamokBuyBackTime);
Runtime.Timers.NextGodzamokCombo = now + Config.Settings.GodzamokCooldown;
},
buyBack: function(targetName) {
const building = Game.Objects[targetName];
const targetAmount = building.amount + Runtime.GodzamokState.soldAmount;
Logger.log('Godzamok', `開始買回 ${targetName}...`);
let bought = 0;
let loops = 0;
while (building.amount < targetAmount && building.price <= Game.cookies && loops < 1000) {
building.buy(1);
bought++;
loops++;
}
if (bought >= Runtime.GodzamokState.soldAmount) {
Logger.success('Godzamok', `已買回 ${bought} 座建築`);
} else {
Logger.warn('Godzamok', `資金不足/迴圈限制,僅買回 ${bought}/${Runtime.GodzamokState.soldAmount}`);
}
Runtime.GodzamokState.isActive = false;
Runtime.GodzamokState.soldAmount = 0;
if (Config.Flags.SpendingLocked) {
$('#chk-spending-lock').prop('checked', false).trigger('change');
}
}
},
// ✅ Feature: Godzamok Tactical Nuke (v8.8.5)
GodzamokTactical: {
fire: function() {
if (Runtime.GodzamokTacticalState.lock) return;
const targetName = Config.Settings.GodzamokTargetBuilding;
const obj = Game.Objects[targetName];
// Whitelist check
if (!obj || ![2, 3, 4].includes(obj.id)) {
Logger.error('Tactical', `非法目標 ${targetName}。僅允許 Farm, Mine, Factory`);
return;
}
Runtime.GodzamokTacticalState.lock = true;
Runtime.GodzamokTacticalState.status = 'COOLDOWN';
UI.updateTacticalButton('ACTIVE');
const amount = obj.amount;
obj.sell(amount); // Sell all using native API, strictly no loop
Logger.log('Tactical', `☢️ 戰術核彈啟動!賣出 ${amount} 座 ${targetName}...`);
// 10s delay
setTimeout(() => {
Logic.GodzamokTactical.reload();
}, 10000);
},
reload: function() {
const targetName = Config.Settings.GodzamokTargetBuilding;
const obj = Game.Objects[targetName];
const setting = Config.Settings.GodzamokBuyback;
const current = obj.amount;
// Phase 3 - Buy Back
if (setting === -1) {
obj.buy(10000); // Buy max behavior
} else if (setting > current) {
const needed = setting - current;
obj.buy(needed);
}
Runtime.GodzamokTacticalState.lock = false;
Runtime.GodzamokTacticalState.status = 'IDLE';
UI.updateTacticalButton('IDLE');
Logger.log('Tactical', `🔄 戰術重裝完成`);
}
},
SugarLump: {
update: function(now) {
const statusEl = $('#lump-status');
if (!Config.Flags.Golden) {
if (statusEl.length) statusEl.text('🍬 糖塊監控:已停用').css('color', '#999').css('border-left-color', '#999');
return;
}
if (typeof Game === 'undefined' || !Game.canLumps()) {
if (statusEl.length) statusEl.text('🍬 糖塊監控:未解鎖').css('color', '#999').css('border-left-color', '#999');
return;
}
const age = Date.now() - Game.lumpT;
const type = Game.lumpCurrentType;
const ripeAge = Game.lumpRipeAge;
let statusText = ''; let statusColor = '#666'; let borderColor = '#ccc'; let action = '';
switch (type) {
case 3:
statusText = '⛔ [肉色糖塊] 啟動保護:等待自然掉落'; statusColor = '#d32f2f'; borderColor = '#d32f2f'; action = 'SKIP';
break;
case 2: case 4:
if (age >= ripeAge) action = 'HARVEST_NOW';
else { statusText = `💎 [稀有糖塊] 等待成熟 (${UI.formatMs(ripeAge - age)})`; statusColor = '#e65100'; borderColor = '#ffd700'; }
break;
case 1:
if (age >= ripeAge) {
if ((Game.lumps / Config.Settings.SugarLumpGoal) > 0.9) action = 'HARVEST_NOW';
else action = 'HARVEST_NOW';
} else { statusText = `🌿 [雙倍糖塊] 等待成熟 (${UI.formatMs(ripeAge - age)})`; statusColor = '#2e7d32'; borderColor = '#4caf50'; }
break;
default:
if (age >= ripeAge) action = 'HARVEST_NOW';
else { statusText = `🍬 [普通糖塊] 等待成熟 (${UI.formatMs(ripeAge - age)})`; statusColor = '#555'; borderColor = '#ccc'; }
break;
}
if (action === 'HARVEST_NOW') {
if (Config.Flags.GlobalMasterSwitch) {
if (Game.lumpCurrentType !== 3) {
Game.clickLump();
Logger.log('SmartLump', `自動收割糖塊 (Type: ${type})`);
statusText = '⚡ 正在收割...'; statusColor = '#4caf50'; borderColor = '#4caf50';
} else {
Logger.warn('SmartLump', '攔截危險操作:試圖點擊肉色糖塊!');
}
}
}
if (statusEl.length) { statusEl.text(statusText).css({ 'color': statusColor, 'border-left-color': borderColor }); }
}
},
Buy: {
update: function(now) {
// 前置條件檢查
if (!Config.Flags.GlobalMasterSwitch || !Game) return;
// 🛡️ [CONSTITUTION LEVEL 0]: 誓約與打寶虛擬鎖定 (Elder Pledge & Virtual Lock)
const pledge = Game.Upgrades['Elder Pledge'];
const isPledgeExpired = (typeof Game.pledgeT === 'undefined' || Game.pledgeT <= 0);
if (Config.Flags.AutoPledge && pledge && pledge.unlocked && pledge.canBuy() && isPledgeExpired) {
// 打寶模式:觸發虛擬鎖定,禁止所有支出(包括誓約)
if (Runtime.SeasonState.isFarming) {
// ⚠️ 關鍵註解,不得移除。QA依此註解進行靜態分析。
// [Constitution] Virtual Lock Block: Farming Mode.
return; // 🔴 憲法級阻斷
} else {
// 一般模式:強制購買誓約
pledge.buy();
return; // 🟢 購買後立即退出
}
}
// 🛡️ [CONSTITUTION REINFORCEMENT]: 二次虛擬鎖定檢查(防 Level 0 漏網)
if (Runtime.SeasonState.isFarming) {
return; // 🔴 憲法級阻斷
}
// [Level 1]: 用戶緊急鎖定 (SpendingLocked)
if (Config.Flags.SpendingLocked) return;
// [Level 2]: 存錢模式 (SavingMode)
if (Config.Flags.SavingMode) return;
// [Level 3]: 常規購買邏輯 (僅當通過以上所有檢查後執行)
if (!Config.Flags.Buy || now < Runtime.Timers.NextBuy) return;
if (Config.Flags.Research) {
const research = document.querySelectorAll('#techUpgrades .crate.upgrade.enabled');
if (research.length > 0) {
const item = research[0];
let resName = "未知科技";
const dataId = item.getAttribute('data-id');
if (dataId && Game.UpgradesById[dataId]) {
resName = Game.UpgradesById[dataId].dname || Game.UpgradesById[dataId].name;
} else {
const onMouseOver = item.getAttribute('onmouseover');
if (onMouseOver) {
const match = /
(.+?)<\/div>/.exec(onMouseOver);
if (match) resName = match[1];
}
}
Logger.log('自動購買', `研發科技:${UI.cleanName(resName)}`);
item.click();
Runtime.Timers.NextBuy = now + Config.Settings.BuyIntervalMs;
return;
}
}
let affordable = Game.UpgradesInStore.filter(u => u.canBuy());
affordable = affordable.filter(u => {
if (u.id === 84) return false;
if (u.id === 85) return false;
if (u.id === 74) return false;
if (u.pool === 'toggle') {
const seasonSwitchIds = [182, 183, 184, 185, 209];
if (!seasonSwitchIds.includes(u.id)) return false;
}
if (Config.Flags.Season) {
const seasonSwitchIds = [182, 183, 184, 185, 209];
if (seasonSwitchIds.includes(u.id)) return false;
}
return true;
});
if (affordable.length > 0) {
if (Config.Settings.BuyStrategy === 'expensive') affordable.sort((a, b) => b.getPrice() - a.getPrice());
else affordable.sort((a, b) => a.getPrice() - b.getPrice());
const target = affordable[0];
let upName = target.dname || target.name;
Logger.log('自動購買-升級', UI.cleanName(upName));
target.buy();
Runtime.Stats.BuyUpgradeCount++;
Runtime.Timers.NextBuy = now + Config.Settings.BuyIntervalMs;
return;
}
if (Config.Flags.Garden) {
const Farm = Game.Objects['Farm'];
if (Farm.minigameLoaded && Farm.minigame) {
const M = Farm.minigame;
let needsSeeds = false;
outerLoop:
for (let y = 0; y < 6; y++) {
for (let x = 0; x < 6; x++) {
if (M.isTileUnlocked(x, y)) {
const savedId = Config.Memory.SavedGardenPlot[y][x];
const currentTile = M.plot[y][x];
if (savedId > -1 && currentTile[0] === 0) {
const seed = M.plantsById[savedId];
if (seed && seed.unlocked) {
needsSeeds = true;
break outerLoop;
}
}
}
}
}
if (needsSeeds) {
if (Math.random() < 0.1) Logger.log('Resource', '資金保護中:優先保留給花園種子');
return;
}
}
}
let affordableBuildings = [];
for (let i in Game.ObjectsById) {
const obj = Game.ObjectsById[i];
if (obj.locked) continue;
if (obj.name === 'Wizard tower' && obj.amount >= Config.Settings.MaxWizardTowers) continue;
if (obj.price <= Game.cookies) affordableBuildings.push(obj);
}
if (affordableBuildings.length > 0) {
if (Config.Settings.BuyStrategy === 'expensive') affordableBuildings.sort((a, b) => b.price - a.price);
else affordableBuildings.sort((a, b) => a.price - b.price);
const targetB = affordableBuildings[0];
let buildName = targetB.displayName || targetB.name;
const domElement = document.getElementById('productName' + targetB.id);
if (domElement) buildName = domElement.innerText;
Logger.log('自動購買-建築', UI.cleanName(buildName));
targetB.buy(1);
Runtime.Stats.BuyBuildingCount++;
Runtime.Timers.NextBuy = now + Config.Settings.BuyIntervalMs;
}
}
},
Wrinkler: {
hasLoggedShiny: false,
update: function(now) {
if (!Config.Flags.AutoWrinkler) return;
for (let i in Game.wrinklers) {
let w = Game.wrinklers[i];
// 1. 保護閃光皺紋蟲 (Shiny, type === 1)
if (w.type === 1) {
if (!this.hasLoggedShiny) {
Logger.success('Wrinkler', '發現閃光皺紋蟲!已啟動保護機制!');
this.hasLoggedShiny = true;
}
continue; // ✅ 絕對保護
}
// 2. 策略選擇
if (Runtime.SeasonState.isFarming) {
w.hp = 0; // 🔴 打寶模式:即時戳破 (Insta-Pop)
} else {
if (w.close === 1) w.hp = 0; // 🟡 一般模式:養肥後戳破
}
}
if (!Game.wrinklers.some(w => w.phase > 0 && w.type === 1)) {
this.hasLoggedShiny = false;
}
}
},
Garden: {
update: function(now) {
if (!Config.Flags.GlobalMasterSwitch) return;
if (!Config.Flags.Garden || now < Runtime.Timers.NextGarden) return;
if (typeof Game === 'undefined') return;
const Farm = Game.Objects['Farm'];
if (!Farm || !Farm.minigameLoaded) return;
const M = Farm.minigame;
if (!M) return;
const isWarmup = Date.now() < Runtime.Timers.GardenWarmup;
if (isWarmup) {
if (Math.random() < 0.05) Logger.warn('花園保護', '暖機緩衝中:跳過鏟除操作');
Runtime.Timers.NextGarden = now + 2500;
return;
}
let isCpsBuffActive = false;
let shouldSkipPlanting = false;
if (Config.Flags.GardenAvoidBuff) {
for (let i in Game.buffs) {
if (Game.buffs[i].multCpS > 1) {
isCpsBuffActive = true;
break;
}
}
if (isCpsBuffActive) {
Config.Memory.LastBuffEndTime = 0;
} else {
const currentTime = Date.now();
const buffEndTime = Config.Memory.LastBuffEndTime || 0;
const bufferTime = Config.Settings.GardenBufferTime || 5000;
if (currentTime - buffEndTime < bufferTime) {
shouldSkipPlanting = true;
}
}
}
if (!isCpsBuffActive && Config.Memory.LastBuffEndTime === 0) {
Config.Memory.LastBuffEndTime = Date.now();
GM_setValue('lastBuffEndTime', Config.Memory.LastBuffEndTime);
}
for (let y = 0; y < 6; y++) {
for (let x = 0; x < 6; x++) {
if (!M.isTileUnlocked(x, y)) continue;
const tile = M.plot[y][x];
const tileId = tile[0];
const tileAge = tile[1];
const savedId = Config.Memory.SavedGardenPlot[y][x];
const normalizedId = (tileId === 0) ? -1 : tileId - 1;
if (normalizedId > -1) {
const plant = M.plantsById[normalizedId];
const isAnomaly = (savedId !== -1 && normalizedId !== savedId) || (savedId === -1);
const plantName = UI.getLocalizedPlantName(tileId);
if (!isAnomaly) {
if (tileAge >= 98 && tileAge >= plant.mature) M.harvest(x, y);
continue;
}
if (Config.Flags.GardenMutation) {
if (plant.unlocked) {
M.harvest(x, y);
Logger.log('花園', `鏟除雜物/已知變異 (紅框): ${plantName}`);
} else {
if (tileAge >= plant.mature) {
M.harvest(x, y);
Logger.success('花園', `成功收割新品種種子 (紫框): ${plantName}`);
}
}
} else {
if (plant.weed) M.harvest(x, y);
}
}
}
}
let allowPlanting = true;
if (Config.Flags.SavingMode && !Config.Flags.SavingReplant) {
allowPlanting = false;
}
if (allowPlanting) {
if (Config.Flags.SyncPlanting) {
let totalCost = 0;
let missingPlants = [];
for (let y = 0; y < 6; y++) {
for (let x = 0; x < 6; x++) {
if (!M.isTileUnlocked(x, y)) continue;
const tile = M.plot[y][x];
const normalizedId = (tile[0] === 0) ? -1 : tile[0] - 1;
const savedId = Config.Memory.SavedGardenPlot[y][x];
if (savedId > -1 && normalizedId === -1) {
const seed = M.plantsById[savedId];
if (seed && seed.unlocked && M.canPlant(seed)) {
totalCost += M.getCost(seed);
missingPlants.push({x, y, id: seed.id});
}
}
}
}
if (Game.cookies < totalCost) {
UI.GardenGrid.updateStatus('funds', totalCost);
Runtime.Timers.NextGarden = now + 2500;
return;
}
if (isCpsBuffActive) {
UI.GardenGrid.updateStatus('buff');
Runtime.Timers.NextGarden = now + 2500;
return;
}
if (missingPlants.length > 0) {
UI.GardenGrid.updateStatus('ready');
for (let p of missingPlants) {
M.useTool(p.id, p.x, p.y);
}
} else {
UI.GardenGrid.updateStatus('ready');
}
} else {
UI.GardenGrid.updateStatus('hide');
for (let y = 0; y < 6; y++) {
for (let x = 0; x < 6; x++) {
if (!M.isTileUnlocked(x, y)) continue;
const tile = M.plot[y][x];
const normalizedId = (tile[0] === 0) ? -1 : tile[0] - 1;
const savedId = Config.Memory.SavedGardenPlot[y][x];
if (normalizedId === -1) {
if (savedId !== -1 && savedId !== null) {
const seed = M.plantsById[savedId];
if (seed && seed.unlocked && M.canPlant(seed)) {
if (Config.Flags.GardenAvoidBuff && (isCpsBuffActive || shouldSkipPlanting)) continue;
M.useTool(seed.id, x, y);
}
}
}
}
}
}
}
Runtime.Timers.NextGarden = now + 2500;
},
updateOverlay: function() {
if (!Config.Flags.GardenOverlay) return;
if (typeof Game === 'undefined' || !Game.Objects['Farm'].minigameLoaded) return;
const M = Game.Objects['Farm'].minigame;
if (!M) return;
for (let y = 0; y < 6; y++) {
for (let x = 0; x < 6; x++) {
const tileDiv = document.getElementById(`gardenTile-${x}-${y}`);
if (!tileDiv) continue;
tileDiv.classList.remove('cc-overlay-missing', 'cc-overlay-anomaly', 'cc-overlay-correct', 'cc-overlay-new');
if (!M.isTileUnlocked(x, y)) continue;
const savedId = Config.Memory.SavedGardenPlot[y][x];
const gameId = M.plot[y][x][0];
const normalizedId = (gameId === 0) ? -1 : gameId - 1;
if (normalizedId === -1 && savedId !== -1) tileDiv.classList.add('cc-overlay-missing');
else if (normalizedId > -1) {
const plant = M.plantsById[normalizedId];
const isAnomaly = (savedId !== -1 && normalizedId !== savedId) || (savedId === -1);
if (isAnomaly) {
if (plant.unlocked) tileDiv.classList.add('cc-overlay-anomaly');
else tileDiv.classList.add('cc-overlay-new');
} else if (normalizedId === savedId) tileDiv.classList.add('cc-overlay-correct');
}
}
}
},
clearOverlay: function() {
$('.cc-overlay-missing, .cc-overlay-anomaly, .cc-overlay-correct, .cc-overlay-new').removeClass('cc-overlay-missing cc-overlay-anomaly cc-overlay-correct cc-overlay-new');
},
saveLayout: function() {
if (typeof Game === 'undefined' || !Game.Objects['Farm'].minigame) { alert('花園未就緒!'); return; }
const M = Game.Objects['Farm'].minigame;
let newLayout = [];
let savedCount = 0;
for (let y = 0; y < 6; y++) {
let row = [];
for (let x = 0; x < 6; x++) {
if (M.isTileUnlocked(x, y)) {
const tile = M.plot[y][x];
const gameId = tile[0];
if (gameId === 0) {
row.push(-1);
} else {
const plantIndex = gameId - 1;
const plant = M.plantsById[plantIndex];
if (plant && plant.unlocked) {
row.push(plantIndex);
savedCount++;
} else {
row.push(-1);
}
}
} else row.push(-1);
}
newLayout.push(row);
}
Config.Memory.SavedGardenPlot = newLayout;
const currentSlot = Config.Memory.GardenSelectedSlot;
const slotData = JSON.stringify(newLayout);
Config.Memory.GardenSavedPlots[currentSlot] = slotData;
GM_setValue('gardenSavedPlots', Config.Memory.GardenSavedPlots);
const btn = $('#garden-save-btn');
const originalText = btn.text();
btn.text('✅ 已儲存!').css('background', '#4caf50');
UI.updateAllLayoutSelectors();
if (typeof Game !== 'undefined' && Game.Notify) Game.Notify('花園陣型已記憶', `已儲存至 Slot ${currentSlot + 1} (${savedCount} 種植物)`, [10, 6], 4);
Logger.success('花園保護', `花園陣型已儲存至 Slot ${currentSlot + 1}`);
setTimeout(() => btn.text(originalText).css('background', '#2196f3'), 1500);
},
loadLayout: function() {
const currentSlot = Config.Memory.GardenSelectedSlot;
const slotString = Config.Memory.GardenSavedPlots[currentSlot];
try {
if (slotString && slotString !== '' && slotString !== '[]') {
const parsed = JSON.parse(slotString);
if (Array.isArray(parsed) && parsed.length === 6) {
Config.Memory.SavedGardenPlot = parsed;
Logger.log('花園保護', `已載入陣型 Slot ${currentSlot + 1}`);
} else {
throw new Error('Invalid Format');
}
} else {
Config.Memory.SavedGardenPlot = Array(6).fill().map(() => Array(6).fill(-1));
Logger.log('花園保護', `Slot ${currentSlot + 1} 為空,已清除目標陣型`);
}
} catch(e) {
Config.Memory.SavedGardenPlot = Array(6).fill().map(() => Array(6).fill(-1));
Logger.warn('花園保護', `Slot ${currentSlot + 1} 數據異常,已重置`);
}
}
},
Stock: {
checkSeedPriority: function() {
if (!Config.Flags.Garden) return false;
const Farm = Game.Objects['Farm'];
if (!Farm.minigameLoaded || !Farm.minigame) return false;
const M = Farm.minigame;
for (let y = 0; y < 6; y++) {
for (let x = 0; x < 6; x++) {
if (M.isTileUnlocked(x, y)) {
const savedId = Config.Memory.SavedGardenPlot[y][x];
const currentTile = M.plot[y][x];
const normalizedId = (currentTile[0] === 0) ? -1 : currentTile[0] - 1;
if (savedId > -1 && normalizedId === -1) {
const seed = M.plantsById[savedId];
if (seed && seed.unlocked) return true;
}
}
}
}
return false;
},
update: function(now) {
if (!Config.Flags.GlobalMasterSwitch) return;
if (!Config.Flags.Stock || now < Runtime.Timers.NextStock) return;
if (Config.Flags.SpendingLocked) {
Runtime.Timers.NextStock = now + 5000;
return;
}
const Bank = Game.Objects['Bank'];
if (!Bank || !Bank.minigameLoaded || !Bank.minigame) return;
if (this.checkSeedPriority()) {
if (Math.random() < 0.05) Logger.log('Stock', '暫停交易:優先保留資金給花園種子');
Runtime.Timers.NextStock = now + 5000;
return;
}
const M = Bank.minigame;
for (let i = 0; i < M.goodsById.length; i++) {
const good = M.goodsById[i];
const price = M.getGoodPrice(good);
const rv = M.getRestingVal(good.id);
const goodName = UI.cleanName(good.name);
if (price < rv * 0.5 && !Config.Flags.SavingMode) {
const maxStock = M.getGoodMaxStock(good);
if (good.stock < maxStock && Game.cookies > price) M.buyGood(good.id, 10000);
}
if (price > rv * 1.5) {
if (good.stock > 0) {
M.sellGood(good.id, 10000);
Logger.log('股市', `獲利賣出 ${goodName} @ $${price.toFixed(2)} (RV: ${rv.toFixed(2)})`);
}
}
}
Runtime.Timers.NextStock = now + 3000;
}
},
Season: {
Requirements: {
'valentines': 7,
'christmas': 7,
'halloween': 7,
'easter': 20
},
update: function(now) {
if (!Config.Flags.GlobalMasterSwitch || !Config.Flags.Season) {
if (Runtime.SeasonState.isFarming) Runtime.SeasonState.isFarming = false;
return;
}
// 0. Boundary & Init
const roadmap = Runtime.SeasonState.Roadmap;
const stageIndex = Runtime.SeasonState.CurrentStage;
if (stageIndex >= roadmap.length) {
if (Runtime.SeasonState.isFarming) {
Runtime.SeasonState.isFarming = false;
Logger.success('Season', '全季節目標達成,自動化掛機中...');
}
return;
}
if (now < Runtime.Timers.NextSeasonCheck) return;
// 1. Lock Target
const currentTask = roadmap[stageIndex];
const targetSeasonId = currentTask.id;
const totalTarget = this.Requirements[targetSeasonId] || 0;
// 2. Inventory Check
let ownedCount = 0;
for (let id in Game.UpgradesById) {
const u = Game.UpgradesById[id];
if (u.season === targetSeasonId && u.bought) {
ownedCount++;
}
}
const isSantaReady = (targetSeasonId !== 'christmas') || (Game.santaLevel >= 14);
// 3. Completion Check (Before Switch)
if (ownedCount >= totalTarget && isSantaReady) {
// ✅ 已畢業:執行智慧跳過
Logger.success('Season', `階段完成: ${currentTask.name} (收集 ${ownedCount}/${totalTarget})`);
Runtime.SeasonState.CurrentStage++;
Runtime.Timers.NextSeasonCheck = now; // 🚀 零延遲,立即檢查下一階
Runtime.SeasonState.isFarming = false;
return; // ⛔ 阻斷後續邏輯,防止錯誤切換
}
// 4. Switch Logic
if (Game.season !== targetSeasonId) {
const switcher = Object.values(Game.Upgrades).find(u => u.toggle && u.season === targetSeasonId);
if (switcher && switcher.canBuy()) {
Logger.log('Season', `切換季節至: ${currentTask.name} (進度: ${ownedCount}/${totalTarget})`);
switcher.buy();
Runtime.Timers.NextSeasonCheck = now + 2000;
} else {
Runtime.Timers.NextSeasonCheck = now + 5000;
}
return;
}
// 5. Farming Logic
Runtime.SeasonState.isFarming = true;
const seasonUpgradesInStore = Game.UpgradesInStore.filter(u => u.season === targetSeasonId && u.canBuy());
if (seasonUpgradesInStore.length > 0) {
seasonUpgradesInStore.forEach(u => {
u.buy();
Logger.log('Season', `購買季節物品: ${u.name}`);
});
Runtime.Timers.NextSeasonCheck = now + 500;
} else {
Runtime.Timers.NextSeasonCheck = now + 2000;
}
}
},
Santa: {
update: function(now) {
if (!Config.Flags.GlobalMasterSwitch) return;
if (!Config.Flags.Santa || Game.season !== 'christmas') return;
if (Game.Has('A festive hat') && !Game.Has('Santa Claus')) {}
if (Game.santaLevel < 14) {
if (typeof Game.UpgradeSanta === 'function') Game.UpgradeSanta();
}
}
},
updateTitle: function() {
if (typeof Game === 'undefined') return;
let totalMult = 1;
let isWorthClicking = false;
if (Game.buffs) {
for (let i in Game.buffs) {
const buff = Game.buffs[i];
if (buff.multCpS > 0) totalMult *= buff.multCpS;
if (buff.multClick > 0) totalMult *= buff.multClick;
if (buff.multClick > 1 || buff.multCpS > 7) isWorthClicking = true;
}
}
let coords = "0,0";
const bigCookie = document.querySelector('#bigCookie');
if (bigCookie) {
const rect = bigCookie.getBoundingClientRect();
coords = `${Math.round(rect.left + rect.width / 2)},${Math.round(rect.top + rect.height / 2)}`;
}
const signal = isWorthClicking ? "⚡ATTACK" : "💤IDLE";
const displayMult = totalMult > 1000 ? (totalMult/1000).toFixed(1) + 'k' : Math.round(totalMult);
document.title = `[${signal}|${displayMult}x|${coords}] ${Runtime.OriginalTitle}`;
}
};
// ═══════════════════════════════════════════════════════════════
// 3. 系統核心 (System Core)
// ═══════════════════════════════════════════════════════════════
const Core = {
heartbeatTimer: null,
waitForGame: function(callback, maxRetries = 100) {
if (typeof Game !== 'undefined' && Game.ready && document.readyState === 'complete') {
callback();
} else if (maxRetries > 0) {
setTimeout(() => { this.waitForGame(callback, maxRetries - 1); }, 100);
} else {
Logger.error('Core', '遊戲初始化超時(10秒),部分功能可能無法使用');
}
},
safeExecute: function(fn, moduleName) {
try {
fn();
} catch(e) {
Logger.error('Core', `${moduleName} 異常`, e);
if (!Runtime.ModuleFailCount) Runtime.ModuleFailCount = {};
Runtime.ModuleFailCount[moduleName] = (Runtime.ModuleFailCount[moduleName] || 0) + 1;
if (Runtime.ModuleFailCount[moduleName] >= 10) {
Logger.critical('Core', `${moduleName} 連續失敗 10 次,已自動停用`);
// Safety Disable
switch(moduleName) {
case 'Buy':
Config.Flags.Buy = false;
GM_setValue('isBuyEnabled', false);
$('#chk-auto-buy').prop('checked', false);
break;
// ... (Other modules)
}
Runtime.ModuleFailCount[moduleName] = 0;
}
}
},
init: function() {
Logger.success('Core', 'Cookie Clicker Ultimate v8.8.5.2 Hotfix2 Loading...');
const checkDataMigration = () => {
const rawPlots = GM_getValue('gardenSavedPlots');
if (!rawPlots) {
Logger.log('Migration', '偵測到舊版數據,正在升級資料結構...');
const newPlots = ['', '', '', '', ''];
try {
const oldData = GM_getValue('savedGardenPlot');
if (oldData && Array.isArray(oldData)) {
newPlots[0] = JSON.stringify(oldData);
Logger.success('Migration', '舊版陣型已遷移至 Slot 1');
}
} catch(e) {
Logger.error('Migration', '舊數據遷移失敗 (Fallback to Empty)', e);
}
GM_setValue('gardenSavedPlots', newPlots);
Config.Memory.GardenSavedPlots = newPlots;
}
};
checkDataMigration();
Logic.Garden.loadLayout();
const safeDelay = Math.max(10000, Config.Settings.BuyIntervalMs);
Runtime.Timers.NextBuy = Date.now() + safeDelay;
Logger.log('Core', `自動購買已延遲 ${UI.formatMs(safeDelay)} 啟動,請利用此時間調整設定`);
Runtime.Timers.GardenWarmup = Date.now() + 10000;
Logger.log('Core', '[花園保護] 暖機模式啟動:暫停操作 10 秒');
const savedSpendingState = GM_getValue('spendingLocked', false);
Config.Flags.SpendingLocked = savedSpendingState;
if (savedSpendingState) {
Logger.warn('Core', '偵測到支出鎖定狀態,已優先啟用保護');
const savedStates = GM_getValue('savedSpendingStates', null);
if (savedStates) {
UI.GardenProtection.SavedStates = savedStates;
}
}
const scriptRestarted = localStorage.getItem('cookieScriptRestarted');
if (scriptRestarted) {
Logger.log('Core', '腳本已自動重啟');
localStorage.removeItem('cookieScriptRestarted');
}
UI.initStyles();
UI.createFloatingButton();
UI.createControlPanel();
UI.createCountdown();
UI.createBuffMonitor();
UI.ActionLog.create();
UI.GardenGrid.create();
UI.Tooltip.create();
// Initial UI Creation
UI.createRightControls();
UI.createTrafficLight();
try {
if (Game.setVolume) Game.setVolume(Config.Settings.Volume);
} catch(e) {
Logger.warn('Core', '音量設定失敗,遊戲可能未完全載入');
}
this.scheduleRestart();
this.startHeartbeat();
setTimeout(() => {
UI.GardenProtection.create();
const restoreLockState = () => {
if ($('#chk-auto-buy').length === 0) {
Logger.warn('花園保護', '主 UI 尚未就緒,延遲恢復狀態');
setTimeout(restoreLockState, 200);
return;
}
if (Config.Flags.SpendingLocked) {
$('#chk-spending-lock').prop('checked', true);
UI.GardenProtection.toggle(true, true);
UI.GardenProtection.updateEmbeddedState();
if (UI.updateEmbeddedState) UI.updateEmbeddedState();
}
};
restoreLockState();
if (Config.Memory.GardenProtectionMinimized) {
setTimeout(() => {
UI.GardenProtection.minimize();
UI.GardenProtection.updateEmbeddedState();
}, 1000);
}
}, 2000);
setTimeout(() => {
Logic.Garden.clearOverlay();
UI.updateButtonState();
UI.ActionLog.toggle(true);
Logger.log('Core', '初始化完成,所有模組已就緒');
}, 3000);
// Input Listener System (v8.8.5)
document.addEventListener('keydown', function(e) {
// Toggle Master Switch
if (e.key === 'F8') {
e.preventDefault();
UI.toggleMasterSwitch();
}
// Godzamok Tactical Nuke Hotkey Parsing
const setting = Config.Settings.GodzamokHotkey;
if (setting) {
const wantCtrl = setting.includes('^');
const wantShift = setting.includes('+');
const wantAlt = setting.includes('!');
if (e.ctrlKey === wantCtrl && e.shiftKey === wantShift && e.altKey === wantAlt) {
let mainKey = setting.replace(/[\^\+\!]/g, '').toUpperCase();
if (e.key.toUpperCase() === mainKey) {
e.preventDefault();
e.stopPropagation();
Logic.GodzamokTactical.fire();
}
}
}
});
window.CookieBot = { UI, Logic, Config, Core, Runtime };
},
startHeartbeat: function() {
const self = this;
const runClicker = () => {
// [Fast Loop] 嚴禁 DOM 查詢,只點擊 Cache
if (Config.Flags.GlobalMasterSwitch && Config.Flags.Click && Runtime.Cache.BigCookie) {
try {
Runtime.Cache.BigCookie.click();
Runtime.Stats.ClickCount++;
} catch(e) {}
}
setTimeout(runClicker, Config.Flags.Click ? Math.max(10, Config.Settings.ClickInterval) : 1000);
};
// [Loop 3] Heartbeat (慢速/DOM/維護)
this.heartbeatTimer = setInterval(() => {
const now = Date.now();
// ✅ 正確順序(不可更改):
Logic.Season.update(now); // 1. 設定 `isFarming` 狀態
Logic.Wrinkler.update(now); // 2. 執行皺紋蟲策略
Logic.Buy.update(now); // 3. 執行購買邏輯(依賴 `isFarming`)
Logic.Garden.update(now); // 4. 其他模組...
Logic.Magic.update(now);
// 1. 維護 Cache (允許 DOM)
const newCookie = document.querySelector('#bigCookie');
Runtime.Cache.BigCookie = newCookie || null;
// 2. 金餅乾點擊
if (Config.Flags.Golden) {
document.querySelectorAll('#shimmers > div.shimmer').forEach(c => c.click());
}
if (!Config.Flags.GlobalMasterSwitch) return;
// 3. 執行所有邏輯 (移入低速迴圈)
self.safeExecute(() => Logic.SugarLump.update(now), 'SugarLump');
self.safeExecute(() => Logic.Stock.update(now), 'Stock');
self.safeExecute(() => Logic.Santa.update(now), 'Santa');
self.safeExecute(() => Logic.Dragon.update(now), 'Dragon');
self.safeExecute(() => Logic.GodzamokCombo.update(now), 'GodzamokCombo');
try { Logic.Garden.updateOverlay(); } catch(e) {}
// 4. UI 更新
if ($('#chk-auto-pledge').length) {
const label = $('#chk-auto-pledge').parent().find('span');
if (Runtime.SeasonState.isFarming) {
label.html('🔄 自動誓約
(打寶中:暫停購買)');
} else {
label.text('🔄 自動購買長者誓約');
}
}
try { Logic.updateTitle(); } catch(e) {}
try { Logic.Click.handlePrompts(); } catch(e) {}
try { UI.updateBuffDisplay(); } catch(e) { console.error(e); }
try { UI.GardenProtection.updateVisibility(); } catch(e) {}
// Grimoire & Tactical Button Injection (Dynamic)
try { UI.createGrimoireControls(); } catch(e) {}
try { UI.createTacticalButton(); } catch(e) {}
if (Config.Flags.ShowGardenGrid && $('#garden-grid-panel').is(':visible')) {
try { UI.GardenGrid.update(); } catch(e) {}
}
const gardenPanel = document.getElementById('gardenPanel');
if (gardenPanel && gardenPanel.style.display !== 'none') {
const embedRight = document.getElementById('cc-embed-right');
if (Config.Memory.GardenProtectionMinimized && Config.Flags.ShowGardenProtection) {
if (!embedRight || embedRight.style.display === 'none') {
const gardenPlot = document.getElementById('gardenPlot');
if (gardenPlot && !embedRight) {
UI.GardenProtection.createEmbeddedControls(gardenPlot);
UI.GardenProtection.updateEmbeddedState();
} else if (embedRight && embedRight.style.display === 'none') {
embedRight.style.display = 'flex';
}
}
}
}
if ($('#cc-embedded-controls').length === 0) {
UI.createRightControls();
}
if (Config.Flags.ShowCountdown) {
$('#txt-rst').text(UI.formatMs(Math.max(0, Runtime.Timers.NextRestart - now)));
$('#txt-buy').text(Config.Flags.Buy ? UI.formatMs(Math.max(0, Runtime.Timers.NextBuy - now)) : '--:--');
}
}, 1000);
// 啟動分離的迴圈
runClicker();
},
scheduleRestart: function() {
if (Runtime.Timers.RestartInterval) clearInterval(Runtime.Timers.RestartInterval);
let interval = Config.Settings.RestartIntervalMs;
if (interval < 60000) interval = 60000;
Runtime.Timers.NextRestart = Date.now() + interval;
if(this.restartTimer) clearTimeout(this.restartTimer);
this.restartTimer = setTimeout(() => this.performRestart(), interval);
},
performRestart: function() {
if (Game.WriteSave) Game.WriteSave();
localStorage.setItem('cookieScriptRestarted', 'true');
GM_openInTab(window.location.href, { active: true, insert: true, setParent: false });
if (this.heartbeatTimer) clearInterval(this.heartbeatTimer);
setTimeout(() => {
window.close();
document.body.innerHTML = `
🔄 系統已重啟
新分頁已開啟,請手動關閉此分頁以節省資源。
(瀏覽器安全設定攔截了自動關閉功能)
`;
document.title = "⛔ 請關閉此分頁";
}, 100);
}
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => { Core.waitForGame(() => Core.init()); });
} else {
Core.waitForGame(() => Core.init());
}
})();