您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
工匠提醒 + 下一个事件 + 商人/商船提醒 + 可隐藏 + 等级阈值缓存
// ==UserScript== // @name 工匠放置小工具之1:事件提醒 // @namespace http://tampermonkey.net/ // @version 1.10 // @description 工匠提醒 + 下一个事件 + 商人/商船提醒 + 可隐藏 + 等级阈值缓存 // @author Stella // @match https://idleartisan.com/* // @grant GM_getValue // @grant GM_setValue // @license CC-BY-NC-SA-4.0 // ==/UserScript== (function() { 'use strict'; const COOLDOWN = 60 * 1000; let cooldownUntil = 0; // 默认配置 const defaultConfig = { CB: true, Siege: true, TS: true, M: true, GLOBAL: true, IDLE: false, MERCHANT_LEVEL: 3 // 默认商人等级阈值 }; const config = {}; for (let k in defaultConfig) { config[k] = GM_getValue(k, defaultConfig[k]); } // 中英文事件表 const eventDict = { "Mining Bonus": "采矿加成", "Woodcutting Bonus": "伐木加成", "Thief": "盗贼", "Battling Bonus": "战斗加成", "Crafting Bonus": "制作加成", "Merchant": "商人", "Purchasing Agent": "采购代理", "Tax Season": "税收季节", "Distant war drums": "遥远的战鼓", "Goblin Siege": "哥布林围攻", "Boss Fight": "Boss对抗", "Ancient Treant": "远古树人", "Runic Golem": "符文魔像", "Trade ship": "贸易船" }; const eventOrder = Object.keys(eventDict); function getLangMode(text) { return /^[\x00-\x7F]*$/.test(text) ? "en" : "zh"; } function getEventName(enName, lang) { return lang === "en" ? enName : (eventDict[enName] || enName); } // ========== UI面板 ========== const panel = document.createElement('div'); panel.style.position = 'fixed'; panel.style.bottom = '20px'; panel.style.right = '20px'; panel.style.background = 'rgba(0,0,0,0.8)'; panel.style.color = 'white'; panel.style.padding = '12px'; panel.style.borderRadius = '10px'; panel.style.zIndex = 9999; panel.style.fontFamily = 'sans-serif'; panel.style.fontSize = '14px'; panel.style.boxShadow = '0 4px 12px rgba(0,0,0,0.5)'; // 隐藏按钮单独一排 const hideContainer = document.createElement('div'); hideContainer.style.textAlign = 'right'; hideContainer.style.marginBottom = '8px'; const closeBtn = document.createElement('span'); closeBtn.textContent = '❌'; closeBtn.style.cursor = 'pointer'; closeBtn.onclick = () => { panel.style.display = 'none'; bulb.style.display = 'block'; }; hideContainer.appendChild(closeBtn); panel.appendChild(hideContainer); function createSwitch(labelText, key) { const container = document.createElement('div'); container.style.marginBottom = '8px'; container.style.display = 'flex'; container.style.alignItems = 'center'; container.style.justifyContent = 'space-between'; const label = document.createElement('span'); label.textContent = labelText; const switchContainer = document.createElement('div'); switchContainer.style.width = '50px'; switchContainer.style.height = '24px'; switchContainer.style.background = config[key] ? '#4caf50' : '#ccc'; switchContainer.style.borderRadius = '12px'; switchContainer.style.position = 'relative'; switchContainer.style.cursor = 'pointer'; switchContainer.style.transition = 'background 0.3s'; const knob = document.createElement('div'); knob.style.width = '20px'; knob.style.height = '20px'; knob.style.background = '#fff'; knob.style.borderRadius = '50%'; knob.style.position = 'absolute'; knob.style.top = '2px'; knob.style.left = config[key] ? '28px' : '2px'; knob.style.transition = 'left 0.3s'; switchContainer.appendChild(knob); switchContainer.addEventListener('click', () => { config[key] = !config[key]; GM_setValue(key, config[key]); switchContainer.style.background = config[key] ? '#4caf50' : '#ccc'; knob.style.left = config[key] ? '28px' : '2px'; }); container.appendChild(label); container.appendChild(switchContainer); panel.appendChild(container); } createSwitch('制作提醒', 'CB'); createSwitch('围攻提醒', 'Siege'); createSwitch('商船提醒', 'TS'); createSwitch('商人提醒', 'M'); // 商人等级阈值输入 const levelContainer = document.createElement('div'); levelContainer.style.marginBottom = '8px'; const levelLabel = document.createElement('span'); levelLabel.textContent = '当物品等级大于X时提醒'; const levelInput = document.createElement('input'); levelInput.type = 'number'; levelInput.min = '1'; levelInput.value = config.MERCHANT_LEVEL; levelInput.style.width = '40px'; levelInput.style.marginLeft = '6px'; levelInput.addEventListener('change', () => { config.MERCHANT_LEVEL = parseInt(levelInput.value, 10) || 1; GM_setValue('MERCHANT_LEVEL', config.MERCHANT_LEVEL); }); levelContainer.appendChild(levelLabel); levelContainer.appendChild(levelInput); panel.appendChild(levelContainer); panel.appendChild(document.createElement('hr')); createSwitch('全局开关', 'GLOBAL'); createSwitch('摸鱼模式', 'IDLE'); document.body.appendChild(panel); // 灯泡按钮 const bulb = document.createElement('div'); bulb.textContent = '💡'; bulb.style.position = 'fixed'; bulb.style.bottom = '20px'; bulb.style.right = '20px'; bulb.style.fontSize = '24px'; bulb.style.cursor = 'move'; bulb.style.zIndex = 10000; bulb.style.display = 'none'; document.body.appendChild(bulb); bulb.addEventListener('click', () => { bulb.style.display = 'none'; panel.style.display = 'block'; }); bulb.onmousedown = function(e) { let shiftX = e.clientX - bulb.getBoundingClientRect().left; let shiftY = e.clientY - bulb.getBoundingClientRect().top; function moveAt(pageX, pageY) { bulb.style.left = pageX - shiftX + 'px'; bulb.style.top = pageY - shiftY + 'px'; bulb.style.right = 'auto'; bulb.style.bottom = 'auto'; } function onMouseMove(e) { moveAt(e.pageX, e.pageY); } document.addEventListener('mousemove', onMouseMove); bulb.onmouseup = function() { document.removeEventListener('mousemove', onMouseMove); bulb.onmouseup = null; }; }; bulb.ondragstart = () => false; // 请求通知权限 if (Notification.permission !== "granted") Notification.requestPermission(); // ========== 定时提醒 ========== setInterval(() => { if (!config.GLOBAL) return; const now = Date.now(); if (now < cooldownUntil) return; const title = document.title.trim(); const langMode = getLangMode(title); const notify = (msg) => { if (config.IDLE) msg = langMode === "en" ? "Windows Update Reminder" : "Windows 更新提醒"; new Notification(config.IDLE ? (langMode === "en" ? "Windows Update" : "Windows 更新") : "Idle Artisan", { body: msg, icon: "https://idleartisan.com/favicon.ico" }); cooldownUntil = now + COOLDOWN; }; // 制作 / 围攻 if (config.CB && (title.includes("CB") || title.includes("制作") || title.includes("Crafting"))) { notify(langMode === "en" ? "Crafting Bonus!" : "制作加成来了!"); } else if (config.Siege && (title.includes("Siege") || title.includes("围攻"))) { notify(langMode === "en" ? "Prepare for Siege!" : "准备 BOSS 战斗!"); } else if (config.TS && (title.includes("TS") || title.includes("商船") || title.includes("Trade ship"))) { // 先切换市场选项卡并选择全部物品 const marketplaceTab = document.getElementById("Marketplace"); const marketFilter = document.getElementById("marketItemFilter"); if (marketplaceTab) marketplaceTab.style.display = "block"; if (marketFilter) { marketFilter.value = "all"; if (typeof updateMarketDisplay === "function") updateMarketDisplay(); } setTimeout(() => { const rows = document.querySelectorAll("#marketListingsDisplay tbody tr"); for (let row of rows) { const seller = row.cells[3]?.textContent || ""; if (seller.includes("[NPC]贸易船")) { const itemName = row.cells[0]?.textContent.trim() || ""; const price = row.cells[2]?.textContent.trim() || ""; notify(`船来!${itemName}@${price}`); break; } } }, 300); } else if (config.M && (title === "Idle Artisan - M" || title.includes("商人") || title.includes("Merchant"))) { const logDisplay = document.getElementById("statusLogDisplay"); if (logDisplay) { const lastLine = logDisplay.innerHTML.split("<br>").reverse().find(line => line.includes("商人来了") || line.includes("Merchant arrived")); if (lastLine) { const match = lastLine.match(/\((\d+)级\)/); const level = match ? parseInt(match[1], 10) : 0; if (level >= config.MERCHANT_LEVEL) { notify(langMode === "en" ? `Merchant arrived! Item Level: ${level}` : `商人来了!物品等级: ${level}`); } } } } }, 10000); // ========== 下一个事件显示 ========== const nextEventLabel = document.createElement('div'); nextEventLabel.style.marginLeft = "15px"; nextEventLabel.style.color = "#ff4d4d"; nextEventLabel.style.fontWeight = "bold"; nextEventLabel.style.fontSize = "14px"; nextEventLabel.textContent = "Next Event: ..."; const eventWrapper = document.getElementById("event-wrapper"); if (eventWrapper && eventWrapper.parentNode) { eventWrapper.parentNode.insertBefore(nextEventLabel, eventWrapper.nextSibling); } setInterval(() => { const currentNameElem = document.getElementById("event-name"); if (!currentNameElem) return; const currentEventRaw = currentNameElem.textContent.trim(); const langMode = getLangMode(currentEventRaw); let currentEn = Object.keys(eventDict).find(en => en === currentEventRaw || eventDict[en] === currentEventRaw); if (!currentEn) return; const idx = eventOrder.findIndex(e => e === currentEn); if (idx >= 0) { const nextEventEn = eventOrder[(idx + 1) % eventOrder.length]; nextEventLabel.textContent = langMode === "en" ? "Next Event: " + getEventName(nextEventEn, langMode) : "下一个事件: " + getEventName(nextEventEn, langMode); } else nextEventLabel.textContent = langMode === "en" ? "Next Event: Unknown" : "下一个事件: 未知"; }, 2000); })();