Automated clicker, auto-buy, auto-harvest, garden manager, stock market trader, and HUD overlay.
当前为
// ==UserScript==
// @name Cookie Clicker Ultimate Automation
// @name:zh-TW 餅乾點點樂全自動掛機輔助 (Cookie Clicker)
// @name:zh-CN 饼干点点乐全自动挂机辅助 (Cookie Clicker)
// @namespace http://tampermonkey.net/
// @version 7.0
// @description Automated clicker, auto-buy, auto-harvest, garden manager, stock market trader, and HUD overlay.
// @description:zh-TW 全功能自動掛機腳本:自動點擊、智慧購買、自動收成糖塊、花園自動補種、股市自動交易、閒置魔法。
// @description:zh-CN 全功能自动挂机脚本:自动点击、智慧购买、自动收成糖块、花园自动补种、股市自动交易、闲置魔法。
// @author You
// @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
// @homepage https://greasyfork.org/zh-CN/scripts/557692-cookie-clicker-ultimate-automation
// @homepageURL https://greasyfork.org/zh-CN/scripts/557692-cookie-clicker-ultimate-automation
// @supportURL https://greasyfork.org/zh-CN/scripts/557692-cookie-clicker-ultimate-automation/feedback
// ==/UserScript==
(function() {
'use strict';
// ═══════════════════════════════════════════════════════════════
// 1. 核心變量 & 狀態記憶
// ═══════════════════════════════════════════════════════════════
// v6.x 基礎功能
let isClickEnabled = GM_getValue('isClickEnabled', false);
let isBuyEnabled = GM_getValue('isBuyEnabled', false);
let isGoldenEnabled = GM_getValue('isGoldenEnabled', false);
let isSpellEnabled = GM_getValue('isSpellEnabled', true); // 基礎連擊魔法
let isGardenEnabled = GM_getValue('isGardenEnabled', false);
let isResearchEnabled = GM_getValue('isResearchEnabled', true);
// v7.0 新增功能
let isStockEnabled = GM_getValue('isStockEnabled', false); // 股市開關
let isSEEnabled = GM_getValue('isSEEnabled', false); // 閒置建築魔法 (Spontaneous Edifice)
// 統計計數
let clickCount = 0;
let buyUpgradeCount = 0;
let buyBuildingCount = 0;
let goldenCookieCount = 0;
// Interval IDs
let intervalId = null;
let buyIntervalId = null;
let goldenCheckIntervalId = null;
let gardenIntervalId = null;
let stockIntervalId = null; // v7.0
let restartIntervalId = null;
let countdownIntervalId = null;
let promptCheckIntervalId = null;
let buffCheckIntervalId = null;
let ahkSignalIntervalId = null;
// 時間控制
let nextRestartTime = 0;
let nextBuyTime = 0;
// 設定參數
let gameVolume = GM_getValue('gameVolume', 50);
let clickInterval = GM_getValue('clickInterval', 50);
let buyStrategy = GM_getValue('buyStrategy', 'expensive');
// 購買間隔
let buyIntervalHours = GM_getValue('buyIntervalHours', 0);
let buyIntervalMinutes = GM_getValue('buyIntervalMinutes', 3);
let buyIntervalSeconds = GM_getValue('buyIntervalSeconds', 0);
// 重啟間隔
let restartIntervalHours = GM_getValue('restartIntervalHours', 1);
let restartIntervalMinutes = GM_getValue('restartIntervalMinutes', 0);
let restartIntervalSeconds = GM_getValue('restartIntervalSeconds', 0);
const MAX_WIZARD_TOWERS = 800;
// 花園陣型記憶
let savedGardenPlot = GM_getValue('savedGardenPlot', Array(6).fill().map(() => Array(6).fill(-1)));
// UI 位置
let showCountdown = GM_getValue('showCountdown', true);
let showBuffMonitor = GM_getValue('showBuffMonitor', true);
let countdownX = GM_getValue('countdownX', window.innerWidth - 170);
let countdownY = GM_getValue('countdownY', 10);
let buffX = GM_getValue('buffX', window.innerWidth - 340);
let buffY = GM_getValue('buffY', 150);
let currentX = GM_getValue('buttonX', 50);
let currentY = GM_getValue('buttonY', 50);
// DOM 元素引用
let countdownDisplay = null;
let buffDisplay = null;
let controlPanel = null;
let floatingButton = null;
const targetSelector = '#bigCookie';
let originalTitle = document.title;
// ═══════════════════════════════════════════════════════════════
// 2. 初始化流程
// ═══════════════════════════════════════════════════════════════
function init() {
console.log('🍪 Cookie Clicker Ultimate Automation v7.0 Loaded');
const scriptRestarted = localStorage.getItem('cookieScriptRestarted');
if (scriptRestarted) {
console.log('🔄 Script restarted automatically.');
localStorage.removeItem('cookieScriptRestarted');
}
if (currentX < 0 || currentX > window.innerWidth) currentX = 50;
createFloatingButton();
createControlPanel();
createCountdownDisplay();
createBuffDisplay();
setGameVolume(gameVolume);
// 啟動各模組循環
goldenCheckIntervalId = setInterval(checkGoldenCookie, 1000); // 含魔法檢查
promptCheckIntervalId = setInterval(autoConfirmPrompt, 1000);
buffCheckIntervalId = setInterval(updateBuffDisplay, 500);
ahkSignalIntervalId = setInterval(updateTitleForAHK, 1000);
gardenIntervalId = setInterval(autoGarden, 2500);
// v7.0 股市循環 (3秒一次即可,股市tick為60秒)
stockIntervalId = setInterval(autoStock, 3000);
startCountdown();
scheduleAutoRestart();
setTimeout(() => {
if (isClickEnabled) startAutoClick();
if (isBuyEnabled) startAutoBuy();
updateAllDisplays();
}, 2000);
document.addEventListener('keydown', function(e) {
if (e.key === 'F8') {
e.preventDefault();
toggleAutoClick();
}
});
}
// ═══════════════════════════════════════════════════════════════
// 3. AHK 通訊協議 (v6.5 Logic)
// ═══════════════════════════════════════════════════════════════
function updateTitleForAHK() {
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();
const cx = Math.round(rect.left + rect.width / 2);
const cy = Math.round(rect.top + rect.height / 2);
coords = `${cx},${cy}`;
}
const signal = isWorthClicking ? "⚡ATTACK" : "💤IDLE";
const displayMult = totalMult > 1000 ? (totalMult/1000).toFixed(1) + 'k' : Math.round(totalMult);
document.title = `[${signal}|${displayMult}x|${coords}] ${originalTitle}`;
}
// ═══════════════════════════════════════════════════════════════
// 4. 核心功能模組 (Click, Garden, Buy)
// ═══════════════════════════════════════════════════════════════
// --- 點擊模組 ---
function autoClick() {
const element = document.querySelector(targetSelector);
if (element) {
element.click();
clickCount++;
}
}
function startAutoClick() {
if (intervalId) clearInterval(intervalId);
intervalId = setInterval(autoClick, clickInterval);
isClickEnabled = true;
updateAllDisplays();
}
function stopAutoClick() {
if (intervalId) clearInterval(intervalId);
isClickEnabled = false;
updateAllDisplays();
}
function toggleAutoClick() {
isClickEnabled = !isClickEnabled;
GM_setValue('isClickEnabled', isClickEnabled);
if (isClickEnabled) startAutoClick();
else stopAutoClick();
}
// --- 花園模組 ---
function autoGarden() {
if (!isGardenEnabled) 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++) {
if (!M.isTileUnlocked(x, y)) continue;
const tile = M.plot[y][x];
const tileId = tile[0];
const tileAge = tile[1];
if (tileId > 0) {
const plant = M.plantsById[tileId - 1];
if (plant.weed || plant.key === 'crumbspore') {
M.harvest(x, y);
continue;
}
if (tileAge >= 98 && tileAge >= plant.mature) {
M.harvest(x, y);
continue;
}
}
if (tileId === 0) {
const targetPlantId = savedGardenPlot[y][x];
if (targetPlantId !== -1 && targetPlantId !== null) {
const seed = M.plantsById[targetPlantId - 1];
if (seed && seed.unlocked && M.canPlant(seed)) {
M.useTool(seed.id, x, y);
}
}
}
}
}
}
function saveGardenLayout() {
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);
}
savedGardenPlot = newLayout;
GM_setValue('savedGardenPlot', savedGardenPlot);
const btn = $('#garden-save-btn');
const originalText = btn.text();
btn.text('✅ 已儲存!').css('background', '#4caf50');
setTimeout(() => btn.text(originalText).css('background', '#2196f3'), 1500);
}
// --- 購買模組 ---
function autoBuy() {
if (typeof Game === 'undefined') return;
// 0. 誓約絕對優先
if (Game.Upgrades['Elder Pledge']) {
const pledge = Game.Upgrades['Elder Pledge'];
if (pledge.unlocked && pledge.bought === 0 && pledge.canBuy()) {
console.log('🛡️ [AutoBuy] 誓約過期,強制後台購買!');
pledge.buy();
nextBuyTime = Date.now() + getBuyInterval();
return;
}
}
// 1. 科技
if (isResearchEnabled) {
const research = document.querySelectorAll('#techUpgrades .crate.upgrade.enabled');
if (research.length > 0) {
const item = research[0];
console.log(`🛒 [購買-科技] 研究: ${getUpgradeName(item)}`);
item.click();
nextBuyTime = Date.now() + getBuyInterval();
return;
}
}
// 2. 升級
const upgradesList = document.querySelectorAll('#upgrades .crate.upgrade.enabled');
if (upgradesList.length > 0) {
const selected = selectByStrategy(upgradesList, true);
if (selected) {
console.log(`🛒 [購買-升級] 購買: ${getUpgradeName(selected)}`);
selected.click();
buyUpgradeCount++;
nextBuyTime = Date.now() + getBuyInterval();
return;
}
}
// 3. 建築
const buildings = document.querySelectorAll('.product.unlocked.enabled');
if (buildings.length > 0) {
const selected = selectByStrategy(buildings, false);
if (selected) {
const buildId = selected.id.replace('product', '');
let buildName = "未知建築";
if(Game.ObjectsById[buildId]) buildName = Game.ObjectsById[buildId].displayName;
console.log(`🛒 [購買-建築] 建造: ${buildName}`);
selected.click();
buyBuildingCount++;
nextBuyTime = Date.now() + getBuyInterval();
}
}
}
function getUpgradeName(element) {
let name = "未知物品";
try {
const id = element.getAttribute('data-id');
if (id && Game.UpgradesById[id]) name = `${Game.UpgradesById[id].name}`;
} catch(e) {}
return name;
}
function selectByStrategy(items, isUpgrade) {
if (!items || items.length === 0) return null;
let itemsArray = Array.from(items);
itemsArray = itemsArray.filter(item => {
const id = item.getAttribute('data-id');
if (isUpgrade && id === '84') return false;
if (!isUpgrade) {
const bId = item.id.replace('product', '');
if (Game.ObjectsById[bId] && Game.ObjectsById[bId].name === 'Wizard tower') {
if (Game.ObjectsById[bId].amount >= MAX_WIZARD_TOWERS) return false;
}
}
return true;
});
if (itemsArray.length === 0) return null;
if (buyStrategy === 'random') return itemsArray[Math.floor(Math.random() * itemsArray.length)];
if (buyStrategy === 'cheapest') return itemsArray[0];
return itemsArray[itemsArray.length - 1];
}
function startAutoBuy() {
if (buyIntervalId) clearInterval(buyIntervalId);
const interval = getBuyInterval();
buyIntervalId = setInterval(autoBuy, interval);
nextBuyTime = Date.now() + interval;
autoBuy();
}
function stopAutoBuy() {
if (buyIntervalId) clearInterval(buyIntervalId);
nextBuyTime = 0;
}
function getBuyInterval() {
return (buyIntervalHours * 3600 + buyIntervalMinutes * 60 + buyIntervalSeconds) * 1000;
}
// ═══════════════════════════════════════════════════════════════
// 5. 股市模組 (v7.0 New)
// ═══════════════════════════════════════════════════════════════
function autoStock() {
if (!isStockEnabled) return;
if (typeof Game === 'undefined') return;
const Bank = Game.Objects['Bank'];
if (!Bank || !Bank.minigameLoaded || !Bank.minigame) return;
const M = Bank.minigame;
// 遍歷所有股票 (ID 0-15)
for (let i = 0; i < M.goodsById.length; i++) {
const good = M.goodsById[i];
const price = M.getGoodPrice(good);
// 獲取靜止價值 (Resting Value)
const rv = M.getRestingVal(good.id);
// 策略:低買高賣 (考慮 20% Overhead)
// 買入閾值:低於 RV 的 50%
// 賣出閾值:高於 RV 的 150% (50% profit margin)
// 1. 買入邏輯
if (price < rv * 0.5) {
// 檢查是否還有庫存空間與現金
const maxStock = M.getGoodMaxStock(good);
if (good.stock < maxStock && Game.cookies > price) {
// 10000 代表買最大值
M.buyGood(good.id, 10000);
// console.log(`📈 [股市] 買入 ${good.name} @ $${price.toFixed(2)} (RV: ${rv.toFixed(2)})`);
}
}
// 2. 賣出邏輯
if (price > rv * 1.5) {
if (good.stock > 0) {
M.sellGood(good.id, 10000); // 賣出所有
console.log(`📉 [股市] 獲利賣出 ${good.name} @ $${price.toFixed(2)} (RV: ${rv.toFixed(2)})`);
}
}
}
}
// ═══════════════════════════════════════════════════════════════
// 6. 金餅乾/糖塊/魔法模組 (v7.0 Updated)
// ═══════════════════════════════════════════════════════════════
function checkGoldenCookie() {
if (isGoldenEnabled) {
autoHarvestLump();
const goldenCookies = document.querySelectorAll('#shimmers > div.shimmer');
if (goldenCookies.length > 0) {
goldenCookies.forEach(cookie => cookie.click());
goldenCookieCount++;
}
}
// 魔法控制 (基礎與進階)
if (isSpellEnabled || isSEEnabled) {
autoCastSpell();
}
}
function autoHarvestLump() {
if (typeof Game === 'undefined') return;
if (Game.canLumps()) {
if (Game.lumpCurrentType === 3) return;
let age = Date.now() - Game.lumpT;
if (age >= Game.lumpRipeAge) {
Game.clickLump();
}
}
}
function autoCastSpell() {
if (typeof Game === 'undefined' || !Game.Objects['Wizard tower'].minigame) return;
const M = Game.Objects['Wizard tower'].minigame;
const spellHoF = M.spells['hand of fate'];
const spellSE = M.spells['spontaneous edifice'];
// --- 優先級 1: 命運之手 (Hand of Fate) ---
// 只有在開啟基礎魔法開關時執行
if (isSpellEnabled && M.magic >= M.getSpellCost(spellHoF)) {
let shouldCast = false;
for (let i in Game.buffs) {
const buff = Game.buffs[i];
if (buff.multCpS > 7 || buff.multClick > 10) { shouldCast = true; break; }
if (buff.multCpS === 7 && M.magic >= M.magicM * 0.95) { shouldCast = true; break; }
}
if (shouldCast) {
M.castSpell(spellHoF);
console.log('🧙♂️ [AutoSpell] 觸發連擊:命運之手');
return; // 施法後直接結束,避免同時施放兩個魔法
}
}
// --- 優先級 2: 憑空建築 (Spontaneous Edifice) ---
// v7.0 新增:閒置期免費獲取建築
if (isSEEnabled && M.magic >= M.getSpellCost(spellSE)) {
// 條件 A: 魔力接近全滿 (避免影響 HoF 回魔)
if (M.magic < M.magicM * 0.95) return;
// 條件 B: 場上沒有 Buff (確保不是連擊準備期)
if (Object.keys(Game.buffs).length > 0) return;
// 條件 C: 場上沒有金餅乾
if (document.querySelectorAll('.shimmer').length > 0) return;
// 施法
console.log('🧙♂️ [AutoSpell] 閒置期:免費召喚了一座建築 (Spontaneous Edifice)');
M.castSpell(spellSE);
}
}
// ═══════════════════════════════════════════════════════════════
// 7. 彈窗確認
// ═══════════════════════════════════════════════════════════════
function autoConfirmPrompt() {
const promptBox = document.querySelector('#prompt');
if (!promptBox) return;
const isVisible = promptBox.style.display !== 'none' && promptBox.offsetHeight > 0;
if (isVisible) {
const promptContent = document.querySelector('#promptContent');
const yesButton = document.querySelector('#promptOption0');
if (promptContent && yesButton) {
const txt = promptContent.textContent;
// 中英雙語關鍵字
const keywords = ['Warning', 'One Mind', 'revoke', '警告', '不好的结果'];
if (keywords.some(k => txt.includes(k))) {
console.log('⚠️ [Safe] Auto-confirming prompt:', txt);
yesButton.click();
}
}
}
}
// ═══════════════════════════════════════════════════════════════
// 8. UI 顯示與管理
// ═══════════════════════════════════════════════════════════════
function createBuffDisplay() {
if (buffDisplay) return;
buffDisplay = $(`
<div id="cookie-buff-monitor" style="
position: fixed; left: ${buffX}px; top: ${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;
box-shadow: 0 4px 20px rgba(0,0,0,0.6);
display: ${showBuffMonitor ? 'block' : 'none'};
cursor: move; user-select: none;
width: 320px; box-sizing: border-box;
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; font-size: 16px; letter-spacing: 1px;
">🔥 Buff 監控</div>
<div id="buff-list-content" style="display: flex; flex-direction: column; gap: 8px;"></div>
</div>
`);
makeDraggable(buffDisplay, 'buffX', 'buffY');
$('body').append(buffDisplay);
}
function updateBuffDisplay() {
if (!showBuffMonitor || !buffDisplay) return;
if (typeof Game === 'undefined') 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 timeLeft = Math.ceil(buff.time / 30);
const iconX = buff.icon[0] * 48;
const iconY = buff.icon[1] * 48;
const iconUrl = 'img/icons.png';
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)';
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; border-radius: 4px;
box-shadow: 0 0 5px rgba(0,0,0,0.5); flex-shrink: 0;
"></div>
<div style="flex: 1; overflow: hidden;">
<div style="font-size: 15px; font-weight: bold; color: ${textColor}; margin-bottom: 2px;">
${buff.dname ? buff.dname : buff.name}
</div>
<div style="font-size: 13px; color: #ffd700;">${multDisplay}</div>
<div style="font-size: 12px; color: #ccc;">剩餘: ${formatTime(timeLeft)}</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);
}
function createCountdownDisplay() {
if (countdownDisplay) return;
countdownDisplay = $(`
<div id="cookie-countdown" style="
position: fixed; left: ${countdownX}px; top: ${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: ${showCountdown ? 'block' : 'none'};
width: 150px; cursor: move; border: 1px solid #444;
">
<div style="text-align: center; border-bottom: 1px solid #555; margin-bottom: 4px;">⏱️ 倒數計時</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">${gameVolume}</span>%</div>
<input type="range" id="volume-slider-mini" min="0" max="100" value="${gameVolume}" style="width:90%; cursor:pointer;">
</div>
</div>
`);
countdownDisplay.find('#volume-slider-mini').on('input', function() {
gameVolume = parseInt($(this).val());
$('#vol-disp').text(gameVolume);
setGameVolume(gameVolume);
GM_setValue('gameVolume', gameVolume);
});
makeDraggable(countdownDisplay, 'countdownX', 'countdownY');
$('body').append(countdownDisplay);
}
function startCountdown() {
if (countdownIntervalId) clearInterval(countdownIntervalId);
countdownIntervalId = setInterval(() => {
if (!showCountdown || !countdownDisplay) return;
const now = Date.now();
const rstRem = Math.max(0, nextRestartTime - now);
const buyRem = Math.max(0, nextBuyTime - now);
$('#txt-rst').text(formatMs(rstRem));
$('#txt-buy').text(isBuyEnabled ? formatMs(buyRem) : '--:--');
if (rstRem < 30000 && rstRem > 0) countdownDisplay.css('background', 'rgba(255,0,0,0.8)');
else countdownDisplay.css('background', 'rgba(0,0,0,0.85)');
}, 1000);
}
function createControlPanel() {
if (controlPanel) return;
const panelX = GM_getValue('panelX', window.innerWidth / 2 - 200);
const panelY = GM_getValue('panelY', 100);
controlPanel = $(`
<div id="cookie-control-panel" style="
position: fixed; left: ${panelX}px; top: ${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>🍪 控制面板 v7.0</span>
<span style="font-size:12px; opacity:0.8;">Ultimate Edition</span>
</div>
<div style="padding: 20px; overflow-y: auto; max-height: calc(85vh - 60px);">
<div class="panel-section" style="margin-bottom:15px; padding:10px; background:#f5f7fa; border-radius:8px;">
<div style="font-weight:bold; color:#222; margin-bottom:10px; border-bottom:2px solid #ddd; padding-bottom:5px;">🎛️ 核心模組開關</div>
<div style="display:grid; grid-template-columns: 1fr 1fr; gap:10px; color:#333;">
<label><input type="checkbox" id="chk-auto-click" ${isClickEnabled?'checked':''}> 👉 自動點擊</label>
<label><input type="checkbox" id="chk-auto-buy" ${isBuyEnabled?'checked':''}> 🛒 自動購買</label>
<label><input type="checkbox" id="chk-auto-golden" ${isGoldenEnabled?'checked':''}> ⭐ 金餅乾/糖塊</label>
<label><input type="checkbox" id="chk-auto-garden" ${isGardenEnabled?'checked':''}> 🌻 花園維護</label>
</div>
</div>
<!-- v7.0 New Features -->
<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;">📈 v7.0 進階掛機</div>
<div style="color:#333; font-size:13px; display:grid; gap:8px;">
<label><input type="checkbox" id="chk-stock" ${isStockEnabled?'checked':''}> 股市自動交易 (低買高賣, RV演算法)</label>
<label><input type="checkbox" id="chk-se" ${isSEEnabled?'checked':''}> 🧙♂️ 閒置魔法: 憑空建築 (免費獲取建築)</label>
</div>
<div style="font-size:11px; color:#555; margin-top:5px;">
* 憑空建築僅在「滿魔、無Buff、無金餅乾」時觸發,不影響連擊。
</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>
<div style="font-size:12px; color:#333; margin-bottom:8px;">包含:除草、收割瀕死植物、自動補種</div>
<button id="garden-save-btn" style="
width:100%; padding:8px; background:#2196f3; color:white; border:none; border-radius:4px; cursor:pointer;
">💾 記憶當前陣型 (用於自動補種)</button>
<div style="font-size:11px; color:#444; margin-top:5px; text-align:center;">
點擊按鈕後,當格子變空時,將自動補種當前位置的植物。
</div>
</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; margin-bottom:5px; color:#333;">
<option value="expensive" ${buyStrategy==='expensive'?'selected':''}>最貴優先 (衝產量)</option>
<option value="cheapest" ${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" style="color:#333;">${generateOptions(0, 59, buyIntervalMinutes, '分')}</select>
<select id="buy-sec" style="color:#333;">${generateOptions(0, 59, buyIntervalSeconds, '秒')}</select>
</div>
<div style="font-size:11px; color:#b71c1c; margin-top:5px; line-height:1.4;">
⚠️ 巫師塔上限: ${MAX_WIZARD_TOWERS}座 (保留魔力上限)<br>
⚠️ 始終拒買: 長者盟約 (Elder Covenant)
</div>
<label style="display:block; margin-top:5px; font-size:12px; color:#333;">
<input type="checkbox" id="chk-research" ${isResearchEnabled?'checked':''}> 包含科技研發
</label>
</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" ${isSpellEnabled?'checked':''}> 🧙♂️ 基礎魔法連擊 (命運之手)</label>
<label style="display:block; font-size:13px; color:#333;"><input type="checkbox" id="chk-ui-count" ${showCountdown?'checked':''}> ⏱️ 倒數計時</label>
<label style="display:block; font-size:13px; color:#333;"><input type="checkbox" id="chk-ui-buff" ${showBuffMonitor?'checked':''}> 🔥 Buff 監控</label>
<div style="margin-top:12px; color:#333;">
<span style="font-size:13px; font-weight:bold;">點擊速度: <span id="spd-val">${clickInterval}</span>ms</span>
<input type="range" id="spd-slider" min="10" max="200" value="${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" style="color:#333;">${generateOptions(0, 24, restartIntervalHours, '時')}</select>
<select id="rst-min" style="color:#333;">${generateOptions(0, 59, restartIntervalMinutes, '分')}</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 style="text-align:center; color:#777; font-size:11px; margin-top:10px;">
Cookie Clicker Ultimate v7.0
</div>
</div>
</div>
`);
makeDraggable(controlPanel, 'panelX', 'panelY', '#panel-header');
$('body').append(controlPanel);
bindPanelEvents();
}
function formatTime(seconds) {
if (seconds < 60) return seconds + "s";
const m = Math.floor(seconds / 60);
const s = seconds % 60;
return `${m}m ${s}s`;
}
function formatMs(ms) {
if (ms <= 0) return "00:00";
const totalSec = Math.floor(ms / 1000);
const h = Math.floor(totalSec / 3600);
const m = Math.floor((totalSec % 3600) / 60);
const s = totalSec % 60;
if (h > 0) return `${h}:${pad(m)}:${pad(s)}`;
return `${pad(m)}:${pad(s)}`;
}
function pad(n) { return n < 10 ? '0'+n : n; }
function generateOptions(min, max, selected, unit) {
let html = '';
for (let i = min; i <= max; i++) html += `<option value="${i}" ${i === selected ? 'selected' : ''}>${i}${unit}</option>`;
return html;
}
function bindPanelEvents() {
$('#chk-auto-click').change(function() {
isClickEnabled = this.checked;
GM_setValue('isClickEnabled', isClickEnabled);
isClickEnabled ? startAutoClick() : stopAutoClick();
});
$('#chk-auto-buy').change(function() {
isBuyEnabled = this.checked;
GM_setValue('isBuyEnabled', isBuyEnabled);
isBuyEnabled ? startAutoBuy() : stopAutoBuy();
});
$('#chk-auto-golden').change(function() {
isGoldenEnabled = this.checked;
GM_setValue('isGoldenEnabled', isGoldenEnabled);
});
$('#chk-auto-garden').change(function() {
isGardenEnabled = this.checked;
GM_setValue('isGardenEnabled', isGardenEnabled);
});
// v7.0 新開關
$('#chk-stock').change(function() {
isStockEnabled = this.checked;
GM_setValue('isStockEnabled', isStockEnabled);
});
$('#chk-se').change(function() {
isSEEnabled = this.checked;
GM_setValue('isSEEnabled', isSEEnabled);
});
$('#garden-save-btn').click(saveGardenLayout);
$('#buy-strategy').change(function() { buyStrategy = $(this).val(); GM_setValue('buyStrategy', buyStrategy); });
$('#chk-research').change(function() { isResearchEnabled = this.checked; GM_setValue('isResearchEnabled', isResearchEnabled); });
$('#chk-spell').change(function() { isSpellEnabled = this.checked; GM_setValue('isSpellEnabled', isSpellEnabled); });
$('#chk-ui-count').change(function() { showCountdown = this.checked; GM_setValue('showCountdown', showCountdown); if(countdownDisplay) countdownDisplay.toggle(showCountdown); });
$('#chk-ui-buff').change(function() { showBuffMonitor = this.checked; GM_setValue('showBuffMonitor', showBuffMonitor); if(buffDisplay) buffDisplay.toggle(showBuffMonitor); });
$('#spd-slider').on('input', function() {
clickInterval = parseInt($(this).val());
$('#spd-val').text(clickInterval);
GM_setValue('clickInterval', clickInterval);
if (isClickEnabled) startAutoClick();
});
$('#buy-min, #buy-sec').change(function() {
buyIntervalMinutes = parseInt($('#buy-min').val());
buyIntervalSeconds = parseInt($('#buy-sec').val());
GM_setValue('buyIntervalMinutes', buyIntervalMinutes);
GM_setValue('buyIntervalSeconds', buyIntervalSeconds);
if (isBuyEnabled) startAutoBuy();
});
$('#rst-hr').change(function() {
restartIntervalHours = parseInt($(this).val());
GM_setValue('restartIntervalHours', restartIntervalHours);
scheduleAutoRestart();
});
$('#rst-min').change(function() {
restartIntervalMinutes = parseInt($(this).val());
GM_setValue('restartIntervalMinutes', restartIntervalMinutes);
scheduleAutoRestart();
});
$('#btn-force-restart').click(function() {
if(confirm('確定要刷新頁面嗎?')) performRestart();
});
}
function makeDraggable(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'));
element.css('cursor', 'grabbing');
});
$(document).on('mousemove', function(e) {
if (isDragging) {
const x = e.clientX - startX;
const y = e.clientY - startY;
element.css({left: x + 'px', top: y + 'px'});
}
}).on('mouseup', function() {
if (isDragging) {
isDragging = false;
element.css('cursor', 'default');
GM_setValue(keyX, parseInt(element.css('left')));
GM_setValue(keyY, parseInt(element.css('top')));
}
});
}
function createFloatingButton() {
if (floatingButton) return;
floatingButton = $(`
<div id="cookie-floating-button" style="
position: fixed; left: ${currentX}px; top: ${currentY}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); user-select: none;
transition: transform 0.1s;
">🍪</div>
`);
floatingButton.click((e) => { e.stopPropagation(); toggleControlPanel(); });
let isD = false;
floatingButton.mousedown(() => isD = true);
$(document).mousemove((e) => {
if(isD) {
currentX = e.clientX - 25; currentY = e.clientY - 25;
floatingButton.css({left: currentX, top: currentY});
}
}).mouseup(() => {
if(isD) { isD = false; GM_setValue('buttonX', currentX); GM_setValue('buttonY', currentY); }
});
$('body').append(floatingButton);
}
function toggleControlPanel() {
if (!controlPanel) createControlPanel();
controlPanel.is(':visible') ? controlPanel.fadeOut(200) : controlPanel.fadeIn(200);
}
function performRestart() {
if (typeof Game !== 'undefined' && 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);
}
function scheduleAutoRestart() {
if (restartIntervalId) clearInterval(restartIntervalId);
let interval = (restartIntervalHours * 3600 + restartIntervalMinutes * 60 + restartIntervalSeconds) * 1000;
if (interval < 60000) interval = 60000;
nextRestartTime = Date.now() + interval;
restartIntervalId = setInterval(performRestart, interval);
}
function setGameVolume(volume) {
try { if (typeof Game !== 'undefined' && Game.setVolume) Game.setVolume(volume); } catch(e) {}
}
function updateAllDisplays() {
if (floatingButton) {
if (isClickEnabled) floatingButton.css('filter', 'hue-rotate(0deg)');
else floatingButton.css('filter', 'grayscale(100%)');
}
}
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', () => setTimeout(init, 1500));
else setTimeout(init, 1500);
})();