// ==UserScript== // @name 《桃源乡》游戏助手 Pro // @namespace http://tampermonkey.net/ // @version 0.3.2 // @author Joyful // @description 上班摸鱼必备的《桃源乡》终极外挂! // @match https://taoyuan.wenzi.games/* // @tag 桃源乡 游戏助手 // @grant none // ==/UserScript== (function() { var d = new Set(); var _virtual_monkey_css_side_effects_default = async (e) => { d.has(e) || (d.add(e), ((t) => { typeof GM_addStyle == "function" ? GM_addStyle(t) : (document.head || document.documentElement).appendChild(document.createElement("style")).append(t); })(e)); }; var STORAGE_KEY = "taoyuan_helper_config"; var defaultConfig = { presets: { preset_1: { id: "preset_1", name: "🌾 默认农场套", hat: "草帽", weapon: "金戟", ring1: "农人青环", ring2: "持久指环", shoe: "疾风靴" }, preset_2: { id: "preset_2", name: "🎣 默认钓鱼套", hat: "渔夫帽", weapon: "竹杖", ring1: "渔翁碧环", ring2: "渔获碧环", shoe: "钓鱼靴" } }, sceneMappings: { "/game/farm": "preset_1", "/game/fishing": "preset_2" }, settings: { autoEquipEnabled: true, xrayEnabled: true, autoFishEnabled: true, autoPauseEnabled: false, fishTension: 5, fishTolerance: 10 } }; var configStore = { data: { ...defaultConfig }, load() { const saved = localStorage.getItem(STORAGE_KEY); if (saved) try { const parsed = JSON.parse(saved); this.data = { presets: parsed.presets || defaultConfig.presets, sceneMappings: { ...defaultConfig.sceneMappings, ...parsed.sceneMappings }, settings: { ...defaultConfig.settings, ...parsed.settings } }; } catch (e) { console.error("配置读取失败,使用默认配置"); } }, save() { localStorage.setItem(STORAGE_KEY, JSON.stringify(this.data)); } }; configStore.load(); var LogLevels = { silent: Number.NEGATIVE_INFINITY, fatal: 0, error: 0, warn: 1, log: 2, info: 3, success: 3, fail: 3, ready: 3, start: 3, box: 3, debug: 4, trace: 5, verbose: Number.POSITIVE_INFINITY }; var LogTypes = { silent: { level: -1 }, fatal: { level: LogLevels.fatal }, error: { level: LogLevels.error }, warn: { level: LogLevels.warn }, log: { level: LogLevels.log }, info: { level: LogLevels.info }, success: { level: LogLevels.success }, fail: { level: LogLevels.fail }, ready: { level: LogLevels.info }, start: { level: LogLevels.info }, box: { level: LogLevels.info }, debug: { level: LogLevels.debug }, trace: { level: LogLevels.trace }, verbose: { level: LogLevels.verbose } }; function isPlainObject$1(value) { if (value === null || typeof value !== "object") return false; const prototype = Object.getPrototypeOf(value); if (prototype !== null && prototype !== Object.prototype && Object.getPrototypeOf(prototype) !== null) return false; if (Symbol.iterator in value) return false; if (Symbol.toStringTag in value) return Object.prototype.toString.call(value) === "[object Module]"; return true; } function _defu(baseObject, defaults, namespace = ".", merger) { if (!isPlainObject$1(defaults)) return _defu(baseObject, {}, namespace, merger); const object = Object.assign({}, defaults); for (const key in baseObject) { if (key === "__proto__" || key === "constructor") continue; const value = baseObject[key]; if (value === null || value === void 0) continue; if (merger && merger(object, key, value, namespace)) continue; if (Array.isArray(value) && Array.isArray(object[key])) object[key] = [...value, ...object[key]]; else if (isPlainObject$1(value) && isPlainObject$1(object[key])) object[key] = _defu(value, object[key], (namespace ? `${namespace}.` : "") + key.toString(), merger); else object[key] = value; } return object; } function createDefu(merger) { return (...arguments_) => arguments_.reduce((p, c) => _defu(p, c, "", merger), {}); } var defu = createDefu(); function isPlainObject(obj) { return Object.prototype.toString.call(obj) === "[object Object]"; } function isLogObj(arg) { if (!isPlainObject(arg)) return false; if (!arg.message && !arg.args) return false; if (arg.stack) return false; return true; } var paused = false; var queue = []; var Consola = class Consola { options; _lastLog; _mockFn; constructor(options = {}) { const types = options.types || LogTypes; this.options = defu({ ...options, defaults: { ...options.defaults }, level: _normalizeLogLevel(options.level, types), reporters: [...options.reporters || []] }, { types: LogTypes, throttle: 1e3, throttleMin: 5, formatOptions: { date: true, colors: false, compact: true } }); for (const type in types) { const defaults = { type, ...this.options.defaults, ...types[type] }; this[type] = this._wrapLogFn(defaults); this[type].raw = this._wrapLogFn(defaults, true); } if (this.options.mockFn) this.mockTypes(); this._lastLog = {}; } get level() { return this.options.level; } set level(level) { this.options.level = _normalizeLogLevel(level, this.options.types, this.options.level); } prompt(message, opts) { if (!this.options.prompt) throw new Error("prompt is not supported!"); return this.options.prompt(message, opts); } create(options) { const instance = new Consola({ ...this.options, ...options }); if (this._mockFn) instance.mockTypes(this._mockFn); return instance; } withDefaults(defaults) { return this.create({ ...this.options, defaults: { ...this.options.defaults, ...defaults } }); } withTag(tag) { return this.withDefaults({ tag: this.options.defaults.tag ? this.options.defaults.tag + ":" + tag : tag }); } addReporter(reporter) { this.options.reporters.push(reporter); return this; } removeReporter(reporter) { if (reporter) { const i = this.options.reporters.indexOf(reporter); if (i !== -1) return this.options.reporters.splice(i, 1); } else this.options.reporters.splice(0); return this; } setReporters(reporters) { this.options.reporters = Array.isArray(reporters) ? reporters : [reporters]; return this; } wrapAll() { this.wrapConsole(); this.wrapStd(); } restoreAll() { this.restoreConsole(); this.restoreStd(); } wrapConsole() { for (const type in this.options.types) { if (!console["__" + type]) console["__" + type] = console[type]; console[type] = this[type].raw; } } restoreConsole() { for (const type in this.options.types) if (console["__" + type]) { console[type] = console["__" + type]; delete console["__" + type]; } } wrapStd() { this._wrapStream(this.options.stdout, "log"); this._wrapStream(this.options.stderr, "log"); } _wrapStream(stream, type) { if (!stream) return; if (!stream.__write) stream.__write = stream.write; stream.write = (data) => { this[type].raw(String(data).trim()); }; } restoreStd() { this._restoreStream(this.options.stdout); this._restoreStream(this.options.stderr); } _restoreStream(stream) { if (!stream) return; if (stream.__write) { stream.write = stream.__write; delete stream.__write; } } pauseLogs() { paused = true; } resumeLogs() { paused = false; const _queue = queue.splice(0); for (const item of _queue) item[0]._logFn(item[1], item[2]); } mockTypes(mockFn) { const _mockFn = mockFn || this.options.mockFn; this._mockFn = _mockFn; if (typeof _mockFn !== "function") return; for (const type in this.options.types) { this[type] = _mockFn(type, this.options.types[type]) || this[type]; this[type].raw = this[type]; } } _wrapLogFn(defaults, isRaw) { return (...args) => { if (paused) { queue.push([ this, defaults, args, isRaw ]); return; } return this._logFn(defaults, args, isRaw); }; } _logFn(defaults, args, isRaw) { if ((defaults.level || 0) > this.level) return false; const logObj = { date: new Date(), args: [], ...defaults, level: _normalizeLogLevel(defaults.level, this.options.types) }; if (!isRaw && args.length === 1 && isLogObj(args[0])) Object.assign(logObj, args[0]); else logObj.args = [...args]; if (logObj.message) { logObj.args.unshift(logObj.message); delete logObj.message; } if (logObj.additional) { if (!Array.isArray(logObj.additional)) logObj.additional = logObj.additional.split("\n"); logObj.args.push("\n" + logObj.additional.join("\n")); delete logObj.additional; } logObj.type = typeof logObj.type === "string" ? logObj.type.toLowerCase() : "log"; logObj.tag = typeof logObj.tag === "string" ? logObj.tag : ""; const resolveLog = (newLog = false) => { const repeated = (this._lastLog.count || 0) - this.options.throttleMin; if (this._lastLog.object && repeated > 0) { const args2 = [...this._lastLog.object.args]; if (repeated > 1) args2.push(`(repeated ${repeated} times)`); this._log({ ...this._lastLog.object, args: args2 }); this._lastLog.count = 1; } if (newLog) { this._lastLog.object = logObj; this._log(logObj); } }; clearTimeout(this._lastLog.timeout); const diffTime = this._lastLog.time && logObj.date ? logObj.date.getTime() - this._lastLog.time.getTime() : 0; this._lastLog.time = logObj.date; if (diffTime < this.options.throttle) try { const serializedLog = JSON.stringify([ logObj.type, logObj.tag, logObj.args ]); const isSameLog = this._lastLog.serialized === serializedLog; this._lastLog.serialized = serializedLog; if (isSameLog) { this._lastLog.count = (this._lastLog.count || 0) + 1; if (this._lastLog.count > this.options.throttleMin) { this._lastLog.timeout = setTimeout(resolveLog, this.options.throttle); return; } } } catch {} resolveLog(true); } _log(logObj) { for (const reporter of this.options.reporters) reporter.log(logObj, { options: this.options }); } }; function _normalizeLogLevel(input, types = {}, defaultLevel = 3) { if (input === void 0) return defaultLevel; if (typeof input === "number") return input; if (types[input] && types[input].level !== void 0) return types[input].level; return defaultLevel; } Consola.prototype.add = Consola.prototype.addReporter; Consola.prototype.remove = Consola.prototype.removeReporter; Consola.prototype.clear = Consola.prototype.removeReporter; Consola.prototype.withScope = Consola.prototype.withTag; Consola.prototype.mock = Consola.prototype.mockTypes; Consola.prototype.pause = Consola.prototype.pauseLogs; Consola.prototype.resume = Consola.prototype.resumeLogs; function createConsola$1(options = {}) { return new Consola(options); } var BrowserReporter = class { options; defaultColor; levelColorMap; typeColorMap; constructor(options) { this.options = { ...options }; this.defaultColor = "#7f8c8d"; this.levelColorMap = { 0: "#c0392b", 1: "#f39c12", 3: "#00BCD4" }; this.typeColorMap = { success: "#2ecc71" }; } _getLogFn(level) { if (level < 1) return console.__error || console.error; if (level === 1) return console.__warn || console.warn; return console.__log || console.log; } log(logObj) { const consoleLogFn = this._getLogFn(logObj.level); const type = logObj.type === "log" ? "" : logObj.type; const tag = logObj.tag || ""; const style = ` background: ${this.typeColorMap[logObj.type] || this.levelColorMap[logObj.level] || this.defaultColor}; border-radius: 0.5em; color: white; font-weight: bold; padding: 2px 0.5em; `; const badge = `%c${[tag, type].filter(Boolean).join(":")}`; if (typeof logObj.args[0] === "string") consoleLogFn(`${badge}%c ${logObj.args[0]}`, style, "", ...logObj.args.slice(1)); else consoleLogFn(badge, style, ...logObj.args); } }; function createConsola(options = {}) { return createConsola$1({ reporters: options.reporters || [new BrowserReporter({})], prompt(message, options2 = {}) { if (options2.type === "confirm") return Promise.resolve(confirm(message)); return Promise.resolve(prompt(message)); }, ...options }); } createConsola(); var logger = createConsola({ level: 2, defaults: { tag: "桃源助手" } }); var spoofPulseTimer = null; function startSpoofPulse() { if (spoofPulseTimer) return; Object.defineProperty(document, "hidden", { configurable: true, get: () => true }); spoofPulseTimer = setInterval(() => { document.dispatchEvent(new Event("visibilitychange")); }, 150); logger.info("🛑 开启高频切后台伪装脉冲,时间线已被强行压制!"); } function stopSpoofPulse() { if (!spoofPulseTimer) return; clearInterval(spoofPulseTimer); spoofPulseTimer = null; delete document.hidden; document.dispatchEvent(new Event("visibilitychange")); logger.info("▶️ 伪装脉冲解除,时间恢复流动。"); } function renderPauseIndicator(isPaused) { let el = document.getElementById("ty-pause-marker"); if (!configStore.data.settings.autoPauseEnabled || !isPaused) { if (el) el.style.display = "none"; return; } if (!el) { el = document.createElement("div"); el.id = "ty-pause-marker"; el.innerHTML = ` `; el.style.cssText = ` position: fixed; top: 25px; /* 避开黄历 */ right: 10px; /* 放右上角 */ transform: translateX(-50%); pointer-events: none; z-index: 9998; /* Flex 居中布局 */ display: flex; align-items: center; justify-content: center; /* 外观 */ background: rgba(0, 0, 0, 0.65); width: 30px; height: 30px; border-radius: 50%; /* 质感 */ backdrop-filter: blur(2px); border: 1px solid rgba(16, 185, 129, 0.3); box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.2); `; document.body.appendChild(el); } el.style.display = "flex"; } function setupAutoPauseService() { logger.info("⏰ 自动暂停 [高频压制版] 已挂载,接管原生离线保护..."); setInterval(() => { const shouldPause = configStore.data.settings.autoPauseEnabled; if (shouldPause && !spoofPulseTimer) { startSpoofPulse(); renderPauseIndicator(true); } else if (!shouldPause && spoofPulseTimer) { stopSpoofPulse(); renderPauseIndicator(false); } }, 1e3); } var TaskManager = { timers: [], observers: [], addInterval(fn, ms) { const id = setInterval(fn, ms); this.timers.push(id); return id; }, addObserver(target, config, callback) { const obs = new MutationObserver(callback); obs.observe(target, config); this.observers.push(obs); return obs; }, clearAll() { this.timers.forEach((id) => clearInterval(id)); this.timers = []; this.observers.forEach((obs) => obs.disconnect()); this.observers = []; logger.info("🧹 [大管家] 已清空当前地图的所有定时器和观察者!"); } }; function setupAutoOperateAll() { TaskManager.addObserver(document.body, { childList: true, subtree: true }, () => { const targetTitle = Array.from(document.querySelectorAll(".game-panel p.text-accent")).find((p) => p.innerText.includes("一键操作")); const targetPanel = targetTitle ? targetTitle.closest(".game-panel") : null; if (!targetPanel) return; const btnContainer = targetPanel.querySelector(".flex.flex-col.space-y-1\\.5"); if (!btnContainer) return; if (btnContainer.querySelector("#helper-auto-all-btn")) return; const myBtn = document.createElement("button"); myBtn.className = "btn text-xs w-full justify-between border border-blue-500/50 bg-blue-500/10 hover:bg-blue-500/20"; myBtn.id = "helper-auto-all-btn"; myBtn.innerHTML = ` ⚡ 执行所有可用操作 `; myBtn.addEventListener("click", () => { const originalBtns = btnContainer.querySelectorAll("button:not(#helper-auto-all-btn)"); let delay = 0; originalBtns.forEach((btn) => { if (!btn.hasAttribute("disabled")) { setTimeout(() => { btn.click(); }, delay); delay += 600; } }); }); btnContainer.insertBefore(myBtn, btnContainer.firstChild); }); } function doFarmWork() { logger.info("进入农村..."); setupAutoOperateAll(); } function calculateSeedProfit(detailText, costText) { const costMatch = costText.match(/(\d+)文/); const priceMatch = detailText.match(/售(\d+)文/); if (!costMatch || !priceMatch) return null; const cost = parseInt(costMatch[1], 10); const price = parseInt(priceMatch[1], 10); const multiMatch = detailText.match(/(\d+)天\s*·\s*每(\d+)天再收/); if (multiMatch) { const firstDays = parseInt(multiMatch[1], 10); const regrowDays = parseInt(multiMatch[2], 10); const remainingDays = 28 - firstDays; const totalHarvests = 1 + (remainingDays >= 0 ? Math.floor(remainingDays / regrowDays) : 0); return { dailyProfit: (totalHarvests * price - cost) / 28, calcType: `季收${totalHarvests}次` }; } const singleMatch = detailText.match(/(\d+)天\s*→/); if (singleMatch) { const days = parseInt(singleMatch[1], 10); return { dailyProfit: (price - cost) / days, calcType: "单茬" }; } return null; } function renderProfitTag(nameElem, profit, calcType) { const tag = document.createElement("span"); tag.className = "profit-tag text-xs font-bold ml-2"; if (profit >= 20) tag.classList.add("text-red-500"); else if (profit >= 13) tag.classList.add("text-orange-500"); else tag.classList.add("text-blue-500"); tag.innerText = `日赚:${profit.toFixed(1)}文 (${calcType})`; nameElem.appendChild(tag); return tag; } function setupSeedProfitAnalyzer() { logger.info("[商店模块] 正在挂载‘万物铺算账’雷达..."); TaskManager.addObserver(document.body, { childList: true, subtree: true }, () => { const shopTitle = document.querySelector("h3.text-accent.text-sm"); if (!shopTitle || !shopTitle.innerHTML.includes("万物铺")) return; const items = document.querySelectorAll(".flex.items-center.justify-between.cursor-pointer"); if (items.length === 0) return; let maxProfit = 0; let bestItem = null; let bestTag = null; for (const item of Array.from(items)) { if (item.querySelector(".profit-tag")) continue; const nameElem = item.querySelector("p.text-sm"); if (!nameElem || !nameElem.innerText.includes("种子")) continue; const result = calculateSeedProfit(item.querySelector(".text-muted")?.innerText || "", item.querySelector("span.text-accent")?.innerText || ""); if (!result || result.dailyProfit <= 0) continue; const tag = renderProfitTag(nameElem, result.dailyProfit, result.calcType); if (result.dailyProfit > maxProfit) { maxProfit = result.dailyProfit; bestItem = item; bestTag = tag; } } if (bestItem && bestTag && !bestItem.classList.contains("border-yellow-500")) { bestItem.classList.replace("border-accent/20", "border-yellow-500"); bestItem.classList.replace("hover:bg-accent/5", "bg-yellow-500/20"); const crown = document.createElement("span"); crown.className = "ml-1 text-yellow-600"; crown.innerText = "👑最优"; bestTag.appendChild(crown); } }); } function doShopWork() { logger.info("商店模块已启动,开始分配任务..."); setupSeedProfitAnalyzer(); } var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); async function startAutoChat(btn) { btn.disabled = true; btn.innerText = "🤖 正在自动聊天中..."; logger.info("[村庄模块] 启动全自动聊天循环!"); let chatCount = 0; const visitedNames = new Set(); while (true) { const targetCard = Array.from(document.querySelectorAll(".grid > .cursor-pointer")).find((card) => { const name = card.querySelector(".text-xs")?.textContent?.trim() || "未知"; return card.querySelector(".lucide-message-circle.text-success") !== null && !visitedNames.has(name); }); if (!targetCard) { logger.info(`[村庄模块] 聊天完毕!本次共与 ${chatCount} 位村民进行了交谈。`); break; } const name = targetCard.querySelector(".text-xs")?.textContent?.trim() || "未知村民"; logger.info(`正在和 [${name}] 攀谈...`); visitedNames.add(name); targetCard.click(); await sleep(500); const panels = document.querySelectorAll(".game-panel"); const currentPanel = panels[panels.length - 1]; if (currentPanel) { let chatBtn; for (let i = 0; i < 10; i++) { chatBtn = Array.from(currentPanel.querySelectorAll("button")).find((b) => b.textContent?.includes("聊天")); if (chatBtn) break; await sleep(200); } if (chatBtn && !chatBtn.disabled) { chatBtn.click(); chatCount++; logger.info(`成功与 [${name}] 聊天!`); await sleep(500); } else logger.warn(`面板里没找到 [${name}] 的聊天按钮,已跳过。`); const closeBtn = Array.from(currentPanel.querySelectorAll("button")).find((b) => b.textContent?.includes("关闭")); if (closeBtn) { closeBtn.click(); await sleep(500); } else { logger.error("找不到关闭按钮,为了安全强行退出循环!"); break; } } } btn.disabled = false; btn.innerHTML = ` ⚡ 一键与全村聊天 `; } function setupVillageAutoChat() { logger.info("[村庄模块] 正在挂载雷达..."); TaskManager.addObserver(document.body, { childList: true, subtree: true }, () => { const villageTitle = document.querySelector("h3.text-accent.text-sm"); if (!villageTitle || !villageTitle.innerHTML.includes("桃源村")) return; const tabContainer = document.querySelector(".flex.space-x-1\\.5.mb-3"); if (!tabContainer) return; if (tabContainer.querySelector("#helper-auto-chat-btn")) return; const myBtn = document.createElement("button"); myBtn.id = "helper-auto-chat-btn"; myBtn.className = "btn text-xs flex-1 justify-center border border-success/50 bg-success/10 hover:bg-success/20"; myBtn.innerHTML = ` ⚡ 一键与全村聊天 `; myBtn.addEventListener("click", () => { startAutoChat(myBtn); }); tabContainer.appendChild(myBtn); }); } function doVillageWork() { logger.info("村庄模块已启动!"); setupVillageAutoChat(); } var scanInterval = null; var isMining = false; function crackPinia() { let vueApp = null; for (const el of Array.from(document.querySelectorAll("*"))) if (el.__vue_app__) { vueApp = el.__vue_app__; break; } if (!vueApp) return null; const provides = vueApp._context?.provides; if (!provides) return null; let pinia = null; for (const sym of Object.getOwnPropertySymbols(provides)) { const instance = provides[sym]; if (instance && instance._s && instance._s instanceof Map) { pinia = instance; break; } } if (!pinia) return null; const miningStore = pinia._s.get("mining"); if (!miningStore) return null; return miningStore.floorGrid; } function renderXRay() { if (!configStore.data.settings.xrayEnabled) { document.querySelectorAll(".ty-xray-marker").forEach((el) => el.remove()); return; } const floorGrid = crackPinia(); if (!floorGrid || floorGrid.length !== 36) return; const gridDiv = document.querySelector(".grid.grid-cols-6"); if (!gridDiv) return; const buttons = gridDiv.querySelectorAll("button"); if (buttons.length !== 36) return; buttons.forEach((btn, index) => { const tile = floorGrid[index]; let rawText = ""; btn.childNodes.forEach((node) => { if (node.nodeType === 3) rawText += node.nodeValue?.trim() || ""; }); const isRevealed = tile.revealed || tile.cleared || tile.isDead || tile.defeated || tile.hp !== void 0 && tile.hp <= 0 || rawText !== "" && rawText !== "?"; let marker = btn.querySelector(".ty-xray-marker"); if (isRevealed) { if (marker) marker.remove(); if (btn.dataset.xray) { btn.style.border = ""; btn.style.backgroundColor = ""; delete btn.dataset.xray; } return; } const type = tile.type || "unknown"; if (btn.dataset.xray === type && marker) return; btn.dataset.xray = type; btn.style.position = "relative"; if (!marker) { marker = document.createElement("span"); marker.className = "ty-xray-marker"; marker.style.cssText = ` position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); pointer-events: none; z-index: 10; display: flex; align-items: center; justify-content: center; width: 100%; height: 100%; font-size: 16px; `; btn.appendChild(marker); } switch (type) { case "monster": case "boss": btn.style.border = "1px solid rgba(239, 68, 68, 0.4)"; btn.style.backgroundColor = "rgba(239, 68, 68, 0.08)"; marker.style.color = "rgba(239, 68, 68, 0.8)"; marker.innerText = "👿"; break; case "trap": btn.style.border = "1px solid rgba(168, 85, 247, 0.4)"; btn.style.backgroundColor = "rgba(168, 85, 247, 0.08)"; marker.innerText = "💣"; break; case "exit": case "stairs": btn.style.border = "1px solid rgba(16, 185, 129, 0.5)"; btn.style.backgroundColor = "rgba(16, 185, 129, 0.1)"; marker.innerText = "🚪"; break; case "mushroom": btn.style.border = "1px solid rgba(217, 70, 239, 0.4)"; btn.style.backgroundColor = "rgba(217, 70, 239, 0.1)"; marker.innerText = "🍄"; break; case "treasure": case "item": case "ore": btn.style.border = "1px solid rgba(245, 158, 11, 0.4)"; btn.style.backgroundColor = "rgba(245, 158, 11, 0.1)"; marker.innerText = "💎"; break; case "empty": btn.style.border = ""; btn.style.backgroundColor = ""; marker.innerText = ""; break; default: btn.style.border = ""; btn.style.backgroundColor = ""; marker.style.opacity = "0.4"; marker.style.fontSize = "10px"; marker.innerText = type; break; } }); } function setupXRayRadar() { if (!configStore.data.settings.xrayEnabled) return; logger.info("👁️ [矿洞模块] 上帝之眼雷达已部署,等待下井..."); TaskManager.addObserver(document.body, { childList: true, subtree: true }, () => { const gridDiv = document.querySelector(".grid.grid-cols-6"); if (gridDiv && !isMining && configStore.data.settings.xrayEnabled) { logger.info("👁️ 进入矿洞层!上帝之眼启动!"); isMining = true; renderXRay(); if (scanInterval) clearInterval(scanInterval); scanInterval = setInterval(renderXRay, 500); } else if ((!gridDiv || !configStore.data.settings.xrayEnabled) && isMining) { logger.info("🛑 离开矿洞层或已手动关闭雷达,透视休眠。"); isMining = false; if (scanInterval) { clearInterval(scanInterval); scanInterval = null; } } }); } function doMineWork() { setupXRayRadar(); } var isFishing = false; var isPressing = false; var animationFrameId = null; var holdLockTimer = null; function pressSpace() { const minHoldTime = (configStore.data.settings.fishTension || 5) * 8; window.dispatchEvent(new KeyboardEvent("keydown", { key: " ", code: "Space", keyCode: 32, bubbles: true })); if (isPressing) return; isPressing = true; if (holdLockTimer) clearTimeout(holdLockTimer); holdLockTimer = setTimeout(() => { holdLockTimer = null; }, minHoldTime); } function releaseSpace(force = false) { if (!isPressing && !force) return; if (holdLockTimer && !force) return; isPressing = false; window.dispatchEvent(new KeyboardEvent("keyup", { key: " ", code: "Space", keyCode: 32, bubbles: true })); if (holdLockTimer) { clearTimeout(holdLockTimer); holdLockTimer = null; } } function trackingLoop() { if (!isFishing || !configStore.data.settings.autoFishEnabled) { releaseSpace(true); return; } const waterArea = document.querySelector(".bg-water\\/20"); if (waterArea) { const playerBar = waterArea.querySelector("[class*=\"bg-success\"]"); const targetFish = waterArea.querySelector("[class*=\"bg-accent\"]"); if (playerBar && targetFish) { const playerCenter = parseFloat(playerBar.style.top || "0") + parseFloat(playerBar.style.height || "0") / 2; const aimTarget = parseFloat(targetFish.style.top || "0") + parseFloat(targetFish.style.height || "0") / 2 - (configStore.data.settings.fishTolerance || 10); if (playerCenter > aimTarget + 1) pressSpace(); else if (playerCenter < aimTarget - 1) releaseSpace(); } else releaseSpace(true); } else releaseSpace(true); animationFrameId = requestAnimationFrame(trackingLoop); } function setupAutoFishing() { if (!configStore.data.settings.autoFishEnabled) return; logger.info("🎣 [钓鱼模块] 自动寻的雷达已部署,等待抛竿..."); TaskManager.addObserver(document.body, { childList: true, subtree: true }, () => { const panel = document.querySelector(".game-panel"); const isFishingPanelOpen = panel && panel.textContent?.includes("实时钓鱼"); if (isFishingPanelOpen && !isFishing && configStore.data.settings.autoFishEnabled) { logger.info(`⚡ 鱼漂异动!自瞄挂引擎点火!当前力度:${configStore.data.settings.fishTension}`); isFishing = true; trackingLoop(); } else if ((!isFishingPanelOpen || !configStore.data.settings.autoFishEnabled) && isFishing) { logger.info("🛑 钓鱼结束或已手动停止,引擎熄火,清理现场。"); isFishing = false; releaseSpace(true); if (animationFrameId) { cancelAnimationFrame(animationFrameId); animationFrameId = null; } } }); } var EQUIP_DICT = { 草帽: "straw_hat", 竹笠: "bamboo_hat", 矿工帽: "miner_helmet", 渔夫帽: "fisher_hat", 铁盔: "iron_helm", 文士帽: "scholar_hat", 药师帽: "herbalist_hat", 商人帽: "merchant_hat", 金冠: "golden_crown", 龙角盔: "dragon_helm", 霜寒兜帽: "frost_hood", 暗影面具: "shadow_mask", 虚空面甲: "void_visor", 石魔帽: "golem_stone_cap", 晶王冠: "crystal_king_crown", 幸运小帽: "lucky_cap", 莲花帽: "lotus_hat", 皮毛帽: "fur_cap", 丝绸头巾: "silk_turban", 翡翠簪: "jade_hairpin", 黑曜盔: "obsidian_helm", 凤冠: "phoenix_crown", 冰后冠冕: "frost_queen_tiara", 龙王角冠: "abyss_dragon_horns", 熔岩兜帽: "lava_helm", 淘金帽: "treasure_cap", 公会战盔: "guild_war_helm", 翠玉护身环: "jade_guard_ring", 石英明环: "quartz_ring", 农人青环: "farmers_ring", 碧灵指环: "jade_spirit_ring", 渔翁碧环: "anglers_ring", 善缘指环: "friendship_ring", 赤焰指环: "ruby_flame_ring", 矿工金环: "miners_ring", 商贾金环: "merchants_ring", 月华指环: "moonlight_ring", 丰月指环: "harvest_moon_ring", 悟道指环: "exp_ring", 暗影指环: "shadow_ring", 寻宝指环: "treasure_hunter_ring", 坚磐指环: "stalwart_ring", 龙脉指环: "dragon_ring", 福运指环: "fortune_ring", 战神指环: "warlord_ring", 五彩天环: "prismatic_ring", 泥岩护带: "mud_golem_band", 冰后指环: "frost_queen_circlet", 熔岩君印: "lava_lord_seal", 持久指环: "endurance_ring", 渔获碧环: "fish_jade_ring", 催生指环: "growth_ring", 行路指环: "travel_ring", 晶王之印: "crystal_king_seal", 暗影君戒: "shadow_sovereign_ring", 龙王指环: "abyss_dragon_ring", 浅矿护环: "shallow_guard", 棱晶护带: "crystal_prism_band", 古玉指环: "ancient_jade_ring", 公会战戒: "guild_war_ring", 草鞋: "straw_sandals", 布鞋: "cloth_shoes", 皮靴: "leather_boots", 矿工靴: "miner_boots", 疾风靴: "gale_boots", 铁甲靴: "iron_greaves", 丝绸绣鞋: "silk_slippers", 商旅靴: "merchant_boots", 月步靴: "moon_step_boots", 龙鳞靴: "dragon_scale_boots", 霜行靴: "frost_treads", 暗影行者: "shadow_striders", 虚空战靴: "void_treads", 熔岩铠靴: "lava_lord_greaves", 暗王之靴: "shadow_sovereign_treads", 福运鞋: "fortune_slippers", 棉鞋: "cotton_shoes", 钓鱼靴: "fishing_waders", 玉底鞋: "jade_slippers", 黑曜甲靴: "obsidian_greaves", 风行靴: "wind_walker", 凤鸣靴: "phoenix_boots", 冰后舞靴: "frost_queen_slippers", 龙王战靴: "abyss_dragon_treads", 晶矿踏靴: "crystal_treads", 幸运长靴: "lucky_boots", 公会战靴: "guild_war_boots", 木棒: "wooden_stick", 铜剑: "copper_sword", 铁刀: "iron_blade", 战锤: "war_hammer", 金戟: "gold_halberd", 骨匕: "bone_dagger", 冰锋匕: "frost_dagger", 暗影之刃: "shadow_blade", 泥王之牙: "mud_king_fang", 冰霜之刺: "frost_queen_sting", 熔岩之锤: "lava_lord_maul", 晶棘匕: "crystal_shard_dagger", 暗影太刀: "shadow_katana", 虚空战锤: "void_hammer", 水晶长剑: "crystal_blade", 暗影锤: "shadow_mace", 虚空太刀: "void_katana", 晶王圣剑: "crystal_king_blade", 暗影之牙: "shadow_sovereign_fang", 龙王权杖: "abyss_dragon_mace", 竹杖: "bamboo_staff", 铁匕: "iron_dagger", 金扇: "golden_fan", 黑曜刀: "obsidian_blade", 粘液锤: "slime_mace", 熔岩刃: "magma_blade", 棱晶匕: "prism_dagger", 虚空之牙: "void_fang_dagger", 翡翠长剑: "jade_sword", 古神剑: "ancient_blade", 公会战刃: "guild_war_blade" }; var REVERSE_EQUIP_DICT = Object.fromEntries(Object.entries(EQUIP_DICT).map(([k, v]) => [v, k])); var SCENE_LIST = [ { name: "农场", path: "/game/farm" }, { name: "畜牧", path: "/game/animal" }, { name: "家园", path: "/game/home" }, { name: "小屋", path: "/game/cottage" }, { name: "村庄", path: "/game/village" }, { name: "商店", path: "/game/shop" }, { name: "采集", path: "/game/forage" }, { name: "钓鱼", path: "/game/fishing" }, { name: "挖矿", path: "/game/mining" }, { name: "烹饪", path: "/game/cooking" }, { name: "育种", path: "/game/breeding" }, { name: "翰海", path: "/game/hanhai" }, { name: "鱼塘", path: "/game/fishpond" } ]; var getDefId = (name) => EQUIP_DICT[name] || name; var currentEquippedPath = null; function showToast(message) { const toast = document.createElement("div"); toast.innerText = message; toast.style.cssText = ` position: fixed; bottom: 20px; right: 20px; background: rgba(16, 185, 129, 0.9); color: white; padding: 8px 16px; border-radius: 4px; font-size: 14px; z-index: 99999; pointer-events: none; box-shadow: 0 4px 6px rgba(0,0,0,0.1); transition: opacity 0.3s; `; document.body.appendChild(toast); setTimeout(() => { toast.style.opacity = "0"; setTimeout(() => toast.remove(), 300); }, 2e3); } function getInventoryStore() { let vueApp = null; const roots = [ document.querySelector("#app"), document.querySelector(".app-wrapper"), document.body ]; for (const root of roots) if (root && root.__vue_app__) { vueApp = root.__vue_app__; break; } if (!vueApp) { const allElements = document.querySelectorAll("*"); for (let i = 0; i < allElements.length; i++) if (allElements[i].__vue_app__) { vueApp = allElements[i].__vue_app__; break; } } if (!vueApp) return null; const provides = vueApp._context?.provides; if (!provides) return null; let pinia = null; const symbols = Object.getOwnPropertySymbols(provides); for (const sym of symbols) { const instance = provides[sym]; if (instance && instance._s && instance._s instanceof Map) { pinia = instance; break; } } if (!pinia) return null; const store = pinia._s.get("inventory"); if (store) return store; return null; } function getOwnedEquipment() { const store = getInventoryStore(); if (!store) return null; const mapToNames = (items) => items.map((i) => REVERSE_EQUIP_DICT[i.defId] || i.defId); return { hats: mapToNames(store.ownedHats || []), weapons: mapToNames(store.ownedWeapons || []), shoes: mapToNames(store.ownedShoes || []), rings: mapToNames(store.ownedRings || []) }; } function autoEquipByVirtualWardrobe(currentPath) { if (!configStore.data.settings.autoEquipEnabled) return; const scene = SCENE_LIST.find((s) => currentPath.includes(s.path)); if (!scene) return; const presetId = configStore.data.sceneMappings[scene.path]; if (!presetId) return; const targetConfig = configStore.data.presets[presetId]; if (!targetConfig) return; if (currentEquippedPath === scene.path) return; const store = getInventoryStore(); if (!store) { logger.warn("🎒 背包模块休眠中,跳过本次换装。(建议进游戏先开一次背包)"); return; } try { logger.info(`开始为 [${scene.name}] 换上套装 [${targetConfig.name}]!`); if (targetConfig.hat) { const trueId = getDefId(targetConfig.hat); const idx = store.ownedHats.findIndex((h) => h.defId === trueId); if (idx !== -1 && store.equippedHatIndex !== idx) store.equipHat(idx); } if (targetConfig.weapon) { const trueId = getDefId(targetConfig.weapon); const idx = store.ownedWeapons.findIndex((w) => w.defId === trueId); if (idx !== -1 && store.equippedWeaponIndex !== idx) store.equipWeapon(idx); } if (targetConfig.shoe) { const trueId = getDefId(targetConfig.shoe); const idx = store.ownedShoes.findIndex((s) => s.defId === trueId); if (idx !== -1 && store.equippedShoeIndex !== idx) store.equipShoe(idx); } let usedRingIndex = -1; let isEquipping = false; const ring1Id = targetConfig.ring1 ? getDefId(targetConfig.ring1) : null; const ring2Id = targetConfig.ring2 ? getDefId(targetConfig.ring2) : null; const ring1Indices = []; const ring2Indices = []; store.ownedRings.forEach((r, index) => { if (r.defId === ring1Id) ring1Indices.push(index); if (r.defId === ring2Id) ring2Indices.push(index); }); if (targetConfig.ring1 && !isEquipping) { isEquipping = true; try { const trueId = getDefId(targetConfig.ring1); const idx = store.ownedRings.findIndex((r) => r.defId === trueId); if (idx !== -1) { store.equipRing(idx, 0); usedRingIndex = idx; } } catch (error) { logger.error("装备戒指1时发生错误:", error); } finally { isEquipping = false; } } if (targetConfig.ring2 && !isEquipping) { isEquipping = true; try { const trueId = getDefId(targetConfig.ring2); const idx = store.ownedRings.findIndex((r, i) => { if (r.defId === trueId) { if (ring1Id === ring2Id && ring1Indices.length === 1) return true; return i !== usedRingIndex; } return false; }); if (idx !== -1) store.equipRing(idx, 1); } catch (error) { logger.error("装备戒指2时发生错误:", error); } finally { isEquipping = false; } } currentEquippedPath = scene.path; showToast(`👗 已自动换装: ${targetConfig.name}`); } catch (error) { logger.error("❌ 换装执行期间发生异常!游戏底层 API 可能已更改:", error); } } function resetEquippedPath() { currentEquippedPath = null; } var rawState = { currentMap: "", isFishing: false }; var listeners = []; var runtimeStore = new Proxy(rawState, { set(target, property, value, receiver) { if (target[property] === value) return true; Reflect.set(target, property, value, receiver); logger.info(`[运行时状态] ${property} 变更为:`, value); listeners.forEach((listener) => listener(property, value)); return true; } }); function watchRuntime(callback) { listeners.push(callback); } _virtual_monkey_css_side_effects_default(":root{--ty-bg:#18181bf2;--ty-panel:#27272ae6;--ty-border:#d4af3740;--ty-accent:#d4af37;--ty-text:#e2e8f0;--ty-muted:#a1a1aa;--ty-radius:4px}.ty-helper-btn{background-color:var(--ty-panel);border:1px solid var(--ty-border);cursor:grab;z-index:9998;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);border-radius:50%;justify-content:center;align-items:center;width:36px;height:36px;font-size:16px;transition:background-color .2s,border-color .2s;display:flex;position:fixed;top:15%;right:20px;box-shadow:0 4px 12px #00000080}.ty-helper-btn:hover{border-color:var(--ty-accent)}.ty-helper-btn:active{cursor:grabbing}.ty-helper-panel{background-color:var(--ty-bg);border:1px solid var(--ty-border);border-radius:var(--ty-radius);z-index:9999;width:260px;max-height:500px;color:var(--ty-text);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);flex-direction:column;font-family:inherit;font-size:12px;display:none;position:fixed;top:15%;right:70px;overflow:hidden;box-shadow:0 8px 32px #0009}.ty-helper-panel.open{display:flex}.ty-panel-header{cursor:grab;-webkit-user-select:none;user-select:none;background-color:#0000004d;border-bottom:1px solid #ffffff0d;justify-content:space-between;align-items:center;padding:10px 12px;display:flex}.ty-panel-header:active{cursor:grabbing}.ty-panel-header h3{color:var(--ty-accent);pointer-events:none;margin:0;font-size:14px;font-weight:700}.ty-panel-close-btn{cursor:pointer;color:var(--ty-muted);background:0 0;border:none;padding:2px 6px;font-size:14px}.ty-panel-close-btn:hover{color:#ef4444}.ty-tabs{border-bottom:1px solid var(--ty-border);background:#0003;display:flex}.ty-tab-btn{color:var(--ty-muted);cursor:pointer;background:0 0;border:none;border-bottom:2px solid #0000;flex:1;padding:8px 0;font-size:12px;transition:all .2s}.ty-tab-btn:hover{color:var(--ty-text);background:#ffffff0d}.ty-tab-btn.active{color:var(--ty-accent);border-bottom-color:var(--ty-accent);font-weight:700}.ty-tab-content{flex:1;padding:12px;overflow-y:auto}.ty-tab-content::-webkit-scrollbar{width:4px}.ty-tab-content::-webkit-scrollbar-thumb{background:var(--ty-border);border-radius:4px}.ty-tab-pane{display:none}.ty-tab-pane.active{animation:.2s fadeIn;display:block}@keyframes fadeIn{0%{opacity:0;transform:translateY(2px)}to{opacity:1;transform:translateY(0)}}.ty-status-empty{text-align:center;color:var(--ty-muted);margin-top:40px}.ty-status-empty .icon{opacity:.5;margin-bottom:8px;font-size:24px}.ty-config-group{background:var(--ty-panel);border-radius:var(--ty-radius);border:1px solid #ffffff0d;margin-bottom:12px;padding:10px}.ty-highlight-group{border-color:var(--ty-border);background:#d4af370d}.ty-config-title{color:var(--ty-accent);border-bottom:1px solid #ffffff0d;margin-bottom:8px;padding-bottom:4px;font-size:13px;font-weight:700}.ty-config-row{justify-content:space-between;align-items:center;margin-bottom:6px;display:flex}.ty-config-row label{color:var(--ty-muted);flex-shrink:0;width:45px}.ty-checkbox-row{cursor:pointer;justify-content:flex-start;gap:8px;margin-bottom:6px}.ty-checkbox-row label{width:auto;color:var(--ty-text);cursor:pointer}.ty-config-row select,.ty-config-row input[type=text]{color:var(--ty-text);background-color:#0000004d;border:1px solid #ffffff1a;border-radius:2px;outline:none;flex:1;padding:4px 6px;font-size:12px;transition:all .2s}.ty-config-row input::placeholder{color:#fff3}.ty-config-row input:focus,.ty-config-row select:focus{border-color:var(--ty-accent);background-color:#00000080}.ty-config-row select option{color:#fff;background-color:#1e293b}.ty-slider{accent-color:var(--ty-accent);flex:1;margin:0 8px}.ty-val-display{text-align:right;width:16px;color:var(--ty-accent);font-weight:700}.ty-save-btn{border:1px solid var(--ty-border);background:var(--ty-panel);width:100%;color:var(--ty-accent);border-radius:var(--ty-radius);cursor:pointer;margin-top:4px;padding:8px;font-weight:700;transition:all .2s}.ty-save-btn:hover{background:var(--ty-accent);color:#000}.ty-config-row.column-style{flex-direction:column;width:100%;margin-bottom:16px;display:flex}.ty-row-header{justify-content:space-between;align-items:center;width:100%;margin-bottom:8px;display:flex}.ty-row-header label{text-align:left;white-space:nowrap;color:#e2e8f0;flex:1;font-size:13px;font-weight:700}.ty-val-display{color:#10b981;border-radius:4px;flex-shrink:0;padding:2px 8px 2px 6px;font-family:monospace;font-size:13px}.ty-slider{cursor:pointer;width:100%;margin:0}"); var layout_default = "
🍑
\r\n\r\n
\r\n
\r\n

🍑 桃源助手

\r\n \r\n
\r\n\r\n
\r\n \r\n \r\n \r\n
\r\n\r\n
\r\n
\r\n
\r\n
📡
\r\n

系统状态监控模块
(等待载入)

\r\n
\r\n
\r\n\r\n
\r\n
\r\n
\r\n
\r\n"; var equip_default = "
\r\n
\r\n \r\n \r\n \r\n ➕\r\n \r\n \r\n 🗑️\r\n \r\n
\r\n
\r\n \r\n \r\n
\r\n
\r\n\r\n
\r\n \r\n 🎒 装备搭配\r\n \r\n 🔄 获取背包\r\n \r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n
\r\n \r\n\r\n\r\n
\r\n
📍 场景绑定配置
\r\n
\r\n\r\n"; var settings_default = "
\r\n
⏰ 时间管理
\r\n
\r\n \r\n \r\n
\r\n
\r\n
\r\n
👔 换装引擎
\r\n
\r\n \r\n \r\n
\r\n\r\n
👁️ 矿洞辅助
\r\n
\r\n \r\n \r\n
\r\n
\r\n\r\n
\r\n
🎣 钓鱼引擎
\r\n
\r\n \r\n \r\n
\r\n\r\n
\r\n
\r\n \r\n 5\r\n
\r\n \r\n
\r\n \r\n
\r\n
\r\n \r\n 10\r\n
\r\n \r\n
\r\n
\r\n\r\n\r\n"; function makeDraggable(element, handle, onClick) { let isDragging = false, startX = 0, startY = 0, initialLeft = 0, initialTop = 0; (handle || element).addEventListener("mousedown", (e) => { const target = e.target; if ([ "BUTTON", "INPUT", "SELECT", "LABEL" ].includes(target.tagName)) return; isDragging = false; startX = e.clientX; startY = e.clientY; const rect = element.getBoundingClientRect(); initialLeft = rect.left; initialTop = rect.top; element.style.transition = "none"; element.style.right = "auto"; element.style.left = initialLeft + "px"; element.style.top = initialTop + "px"; document.addEventListener("mousemove", onMouseMove); document.addEventListener("mouseup", onMouseUp); }); const onMouseMove = (e) => { const dx = e.clientX - startX, dy = e.clientY - startY; if (Math.abs(dx) > 5 || Math.abs(dy) > 5) isDragging = true; if (isDragging) { element.style.left = initialLeft + dx + "px"; element.style.top = initialTop + dy + "px"; } }; const onMouseUp = () => { document.removeEventListener("mousemove", onMouseMove); document.removeEventListener("mouseup", onMouseUp); element.style.transition = ""; if (!isDragging && onClick) onClick(); }; } function initEquipTab(container) { const presetSelector = container.querySelector("#equip-preset-selector"); const presetNameInput = container.querySelector("#eq-preset-name"); const btnAddPreset = container.querySelector("#btn-add-preset"); const btnDelPreset = container.querySelector("#btn-del-preset"); const btnRefreshInv = container.querySelector("#btn-refresh-inv"); const btnSaveEquip = container.querySelector("#btn-save-equip"); const sceneContainer = container.querySelector("#scene-mapping-container"); const eqSelectors = { hat: container.querySelector("#eq-hat"), weapon: container.querySelector("#eq-weapon"), ring1: container.querySelector("#eq-ring1"), ring2: container.querySelector("#eq-ring2"), shoe: container.querySelector("#eq-shoe") }; const renderOptions = () => { const inv = getOwnedEquipment(); Object.entries(eqSelectors).forEach(([key, sel]) => { const savedVal = sel.dataset.val || ""; let html = ""; if (!inv) { html = ``; if (savedVal) html = `` + html; } else { html = ``; const items = inv[key + "s"] || []; const uniqueItems = Array.from(new Set(items)); if (savedVal && !uniqueItems.includes(savedVal)) uniqueItems.unshift(savedVal); uniqueItems.forEach((item) => { html += ``; }); } sel.innerHTML = html; sel.value = savedVal; }); if (btnRefreshInv) btnRefreshInv.style.color = inv ? "#10b981" : "#ef4444"; }; const renderPresetList = (selectId) => { presetSelector.innerHTML = ""; Object.entries(configStore.data.presets).forEach(([id, preset]) => { const opt = document.createElement("option"); opt.value = id; opt.textContent = preset.name; presetSelector.appendChild(opt); }); if (selectId) presetSelector.value = selectId; loadPresetToForm(); renderSceneMappings(); }; const loadPresetToForm = () => { const id = presetSelector.value; const preset = configStore.data.presets[id]; if (!preset) return; presetNameInput.value = preset.name; eqSelectors.hat.dataset.val = preset.hat || ""; eqSelectors.weapon.dataset.val = preset.weapon || ""; eqSelectors.ring1.dataset.val = preset.ring1 || ""; eqSelectors.ring2.dataset.val = preset.ring2 || ""; eqSelectors.shoe.dataset.val = preset.shoe || ""; renderOptions(); }; const renderSceneMappings = () => { if (!sceneContainer) return; sceneContainer.innerHTML = ""; const presetOptions = `` + Object.entries(configStore.data.presets).map(([id, p]) => ``).join(""); SCENE_LIST.forEach((scene) => { const row = document.createElement("div"); row.className = "ty-config-row"; row.innerHTML = ` `; const select = row.querySelector("select"); select.value = configStore.data.sceneMappings[scene.path] || ""; select.addEventListener("change", (e) => { const val = e.target.value; if (val) configStore.data.sceneMappings[scene.path] = val; else delete configStore.data.sceneMappings[scene.path]; configStore.save(); resetEquippedPath(); }); sceneContainer.appendChild(row); }); }; presetSelector.addEventListener("change", loadPresetToForm); btnRefreshInv?.addEventListener("click", () => renderOptions()); btnAddPreset?.addEventListener("click", () => { const newId = "preset_" + Date.now(); configStore.data.presets[newId] = { id: newId, name: "新方案 " + Math.floor(Math.random() * 1e3), hat: "", weapon: "", ring1: "", ring2: "", shoe: "" }; configStore.save(); renderPresetList(newId); }); btnDelPreset?.addEventListener("click", () => { const id = presetSelector.value; if (Object.keys(configStore.data.presets).length <= 1) return alert("至少保留一个方案"); if (!confirm(`确认删除方案 [${configStore.data.presets[id].name}]?`)) return; delete configStore.data.presets[id]; Object.keys(configStore.data.sceneMappings).forEach((path) => { if (configStore.data.sceneMappings[path] === id) delete configStore.data.sceneMappings[path]; }); configStore.save(); renderPresetList(Object.keys(configStore.data.presets)[0]); }); btnSaveEquip?.addEventListener("click", () => { const id = presetSelector.value; const updatedPreset = { id, name: presetNameInput.value.trim() || "未命名方案", hat: eqSelectors.hat.value, weapon: eqSelectors.weapon.value, ring1: eqSelectors.ring1.value, ring2: eqSelectors.ring2.value, shoe: eqSelectors.shoe.value }; configStore.data.presets[id] = updatedPreset; configStore.save(); btnSaveEquip.innerText = "✅ 已保存"; btnSaveEquip.style.backgroundColor = "#10b981"; setTimeout(() => { btnSaveEquip.innerText = "💾 保存当前套装"; btnSaveEquip.style.backgroundColor = ""; }, 1500); renderPresetList(id); resetEquippedPath(); }); renderPresetList(); return { refreshInv: renderOptions, refreshScenes: renderSceneMappings }; } function initSettingsTab(container) { const swAutoPause = container.querySelector("#switch-autopause"); const swAutoEquip = container.querySelector("#switch-autoequip"); const swXRay = container.querySelector("#switch-xray"); const swAutoFish = container.querySelector("#switch-autofish"); const sliderTension = container.querySelector("#slider-tension"); const sliderTolerance = container.querySelector("#slider-tolerance"); const valTension = container.querySelector("#val-tension"); const valTolerance = container.querySelector("#val-tolerance"); const btnSave = container.querySelector("#btn-save-settings"); const syncSliderValues = () => { if (valTension && sliderTension) valTension.textContent = sliderTension.value; if (valTolerance && sliderTolerance) valTolerance.textContent = sliderTolerance.value; }; if (sliderTension) sliderTension.addEventListener("input", syncSliderValues); if (sliderTolerance) sliderTolerance.addEventListener("input", syncSliderValues); const load = () => { const s = configStore.data.settings; if (swAutoPause) swAutoPause.checked = s.autoPauseEnabled || false; if (swAutoEquip) swAutoEquip.checked = s.autoEquipEnabled || true; if (swXRay) swXRay.checked = s.xrayEnabled || true; if (swAutoFish) swAutoFish.checked = s.autoFishEnabled || true; if (sliderTension) sliderTension.value = s.fishTension.toString(); if (sliderTolerance) sliderTolerance.value = s.fishTolerance.toString(); syncSliderValues(); }; btnSave?.addEventListener("click", () => { configStore.data.settings.autoPauseEnabled = swAutoPause.checked; configStore.data.settings.autoEquipEnabled = swAutoEquip.checked; configStore.data.settings.xrayEnabled = swXRay.checked; configStore.data.settings.autoFishEnabled = swAutoFish.checked; configStore.data.settings.fishTension = parseInt(sliderTension.value, 10); configStore.data.settings.fishTolerance = parseInt(sliderTolerance.value, 10); configStore.save(); }); load(); } function initPanel() { if (document.getElementById("ty-bot-container")) return; const container = document.createElement("div"); container.id = "ty-bot-container"; container.innerHTML = layout_default; document.body.appendChild(container); container.querySelector("#pane-equip").innerHTML = equip_default; container.querySelector("#pane-settings").innerHTML = settings_default; const equipModule = initEquipTab(container); initSettingsTab(container); const btn = container.querySelector(".ty-helper-btn"); const panel = container.querySelector(".ty-helper-panel"); makeDraggable(btn, void 0, () => { panel.classList.toggle("open"); if (panel.classList.contains("open")) { equipModule.refreshInv(); equipModule.refreshScenes(); } }); makeDraggable(panel, container.querySelector(".ty-panel-header")); container.querySelector(".ty-panel-close-btn")?.addEventListener("click", (e) => { e.stopPropagation(); panel.classList.remove("open"); }); container.querySelectorAll(".ty-tab-btn").forEach((btn) => { btn.addEventListener("click", (e) => { const targetId = e.currentTarget.dataset.target; container.querySelectorAll(".ty-tab-btn, .ty-tab-pane").forEach((el) => el.classList.remove("active")); btn.classList.add("active"); container.querySelector(`#${targetId}`).classList.add("active"); }); }); } function calculateAlmanac(season, weather) { if (weather.includes("晴")) return { yi: [{ text: "诸事皆宜", weight: 100, color: "#f59e0b" }], ji: [] }; const yi = []; const ji = []; if (weather.includes("雨") && !weather.includes("雷") && !weather.includes("绿")) { yi.push({ text: "农田免浇", weight: 40 }, { text: "鱼类提质", weight: 30 }, { text: "淘金", weight: 20 }, { text: "采集(+15%)", weight: 15 }); if (season.includes("春")) yi.push({ text: "钓翠龙", weight: 25 }); else if (season.includes("秋")) yi.push({ text: "钓鲈/鳗", weight: 25 }); else if (season.includes("冬")) yi.push({ text: "钓岩鳗", weight: 25 }); else yi.push({ text: "钓泥鳅", weight: 10 }); } if (weather.includes("雷")) { ji.push({ text: "矿洞探索", weight: 30 }, { text: "采集(-20%)", weight: 20 }, { text: "出远门", weight: 10 }); if (season.includes("夏")) yi.push({ text: "钓龙鱼/江龙", weight: 30 }); else yi.push({ text: "钓龙鱼", weight: 15 }); } if (weather.includes("雪")) { ji.push({ text: "采集(-10%)", weight: 10 }); if (season.includes("冬")) yi.push({ text: "钓冰/娃鱼", weight: 30 }); else yi.push({ text: "钓冰鱼", weight: 15 }); } if (weather.includes("风")) { yi.push({ text: "采集(+10%)", weight: 10 }); if (season.includes("夏")) yi.push({ text: "钓飞鱼", weight: 25 }); else yi.push({ text: "钓溪鲑", weight: 15 }); } if (weather.includes("绿")) yi.push({ text: "采集(+50%)", weight: 50 }); yi.sort((a, b) => b.weight - a.weight); ji.sort((a, b) => b.weight - a.weight); yi.forEach((item) => { if (item.weight >= 40) item.color = "#f59e0b"; else if (item.weight >= 25) item.color = "#10b981"; else item.color = "#34d399"; }); ji.forEach((item) => { if (item.weight >= 30) item.color = "#ef4444"; else item.color = "#f87171"; }); return { yi, ji }; } function renderAlmanacUI() { try { const titleSpans = document.querySelectorAll("span.text-accent.font-bold"); let activeTitleSpan = null; for (const span of Array.from(titleSpans)) if (span.textContent?.includes("桃源乡") && span.getBoundingClientRect().width > 0) { activeTitleSpan = span; break; } let container = document.getElementById("ty-almanac-container"); if (!activeTitleSpan || !activeTitleSpan.parentElement) { if (container) container.style.display = "none"; return; } const topBar = activeTitleSpan.parentElement; let season = ""; let weather = ""; let weatherSpan = null; for (const child of Array.from(topBar.children)) { const text = child.textContent?.trim() || ""; if ((text.includes("春") || text.includes("夏") || text.includes("秋") || text.includes("冬")) && text.includes("第")) season = text; if ([ "晴", "雨", "雷", "雪", "风", "绿" ].some((w) => text.includes(w)) && text.length <= 4) { weather = text; weatherSpan = child; } } if (!season || !weather || !weatherSpan) { if (container) container.style.display = "none"; return; } if (!container) { container = document.createElement("div"); container.id = "ty-almanac-container"; container.style.cssText = ` position: fixed; /* 绝对悬浮,无视页面滚动 */ pointer-events: none; /* 绝对不能阻挡玩家鼠标点击 */ display: inline-flex; gap: 6px; font-size: 0.75rem; font-weight: 700; user-select: none; z-index: 9999; /* 保证不被其他东西挡住 */ transition: opacity 0.2s; /* 切换时更平滑 */ `; document.body.appendChild(container); } const rect = weatherSpan.getBoundingClientRect(); if (rect.width > 0) { container.style.display = "inline-flex"; container.style.left = `${rect.right + window.scrollX + 10}px`; container.style.top = `${rect.top + window.scrollY + rect.height / 2}px`; container.style.transform = `translateY(-50%)`; } else container.style.display = "none"; const currentKey = `${season}-${weather}`; if (container.dataset.key === currentKey && container.innerHTML !== "") return; container.dataset.key = currentKey; const { yi, ji } = calculateAlmanac(season, weather); let html = ""; if (yi.length > 0) { html += ` `; yi.forEach((item) => html += `${item.text}`); html += ``; } if (ji.length > 0) { html += ` `; ji.forEach((item) => html += `${item.text}`); html += ``; } container.innerHTML = html; } catch (e) { console.error("[桃源助手] 黄历渲染错误:", e); } } function setupAlmanacObserver() { logger.info("启动黄历观测局 (已开启绝对物理隔离模式)..."); let lastTime = 0; function almanacLoop(timestamp) { requestAnimationFrame(almanacLoop); if (timestamp - lastTime < 500) return; lastTime = timestamp; renderAlmanacUI(); } requestAnimationFrame(almanacLoop); } watchRuntime((key, newValue) => { if (key === "currentMap") { const currentPath = newValue; if (configStore.data.settings.autoEquipEnabled) { autoEquipByVirtualWardrobe(currentPath); logger.info("✅ 智能换装已启动,当前穿着已更新。"); } else logger.info("👔 智能换装已关闭,保留当前穿着。"); TaskManager.clearAll(); setTimeout(() => { if (currentPath.includes("/village")) doVillageWork(); else if (currentPath.includes("/farm")) doFarmWork(); else if (currentPath.includes("/shop")) doShopWork(); else if (currentPath.includes("/fishing")) if (configStore.data.settings.autoFishEnabled) { logger.info("🎣 检测到进入钓鱼场,自动钓鱼模块启动!"); setupAutoFishing(); } else logger.info("🎣 进入钓鱼场,已读取到设置:自动钓鱼处于关闭状态。"); else if (currentPath.includes("/mining") || currentPath.includes("/mine")) doMineWork(); }, 500); } }); function hijackGameRouter() { const getCurrentPath = () => window.location.hash || window.location.pathname; const originalPushState = history.pushState; history.pushState = function(...args) { originalPushState.apply(this, args); runtimeStore.currentMap = getCurrentPath(); }; const originalReplaceState = history.replaceState; history.replaceState = function(...args) { originalReplaceState.apply(this, args); runtimeStore.currentMap = getCurrentPath(); }; window.addEventListener("popstate", () => { runtimeStore.currentMap = getCurrentPath(); }); window.addEventListener("hashchange", () => { runtimeStore.currentMap = getCurrentPath(); }); runtimeStore.currentMap = getCurrentPath(); } function main() { console.log("%c 桃源助手 %c 注入成功 %c v1.0.0 ", "color: #fff; background: #10b981; padding: 2px 4px; border-radius: 3px 0 0 3px; font-weight: bold;", "color: #fff; background: #444; padding: 2px 4px; font-weight: bold;", "color: #10b981; background: #eee; padding: 2px 4px; border-radius: 0 3px 3px 0; font-weight: bold;"); initPanel(); setupAlmanacObserver(); setupAutoPauseService(); hijackGameRouter(); } main(); })();