// ==UserScript== // @name 《桃源乡》游戏助手 Pro // @namespace http://tampermonkey.net/ // @version 0.2.0 // @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 logger = { info: (msg, ...args) => { console.log(`%c[桃源助手] ${msg}`, "color: #3b82f6;", ...args); }, success: (msg, ...args) => { console.log(`%c[桃源助手] ✨ ${msg}`, "color: #10b981; font-weight: bold;", ...args); }, warn: (msg, ...args) => { console.warn(`[桃源助手] ⚠️ ${msg}`, ...args); }, error: (msg, ...args) => { console.error(`[桃源助手] ❌ ${msg}`, ...args); } }; 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 STORAGE_KEY = "taoyuan_helper_config"; var defaultConfig = { equipPresets: { "/farm": { name: "🌾 农场专属", hat: "草帽", weapon: "金戟", ring1: "农人青环", ring2: "持久指环", shoe: "疾风靴" }, "/fish": { name: "🎣 钓鱼专属", hat: "渔夫帽", weapon: "竹杖", ring1: "渔翁碧环", ring2: "渔获碧环", shoe: "钓鱼靴" }, "/shop": { name: "💰 商人专属", hat: "商人帽", weapon: "金扇", ring1: "商贾金环", ring2: "古玉指环", shoe: "商旅靴" }, "/mining": { name: "⛏️ 矿工专属", hat: "矿工帽", weapon: "战锤", ring1: "矿工金环", ring2: "翠玉护身环", shoe: "矿工靴" } }, settings: { autoEquipEnabled: true, xrayEnabled: true, autoFishEnabled: true, 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 = { equipPresets: { ...defaultConfig.equipPresets, ...parsed.equipPresets }, settings: { ...defaultConfig.settings, ...parsed.settings } }; } catch (e) { console.error("配置读取失败,使用默认配置"); } }, save() { localStorage.setItem(STORAGE_KEY, JSON.stringify(this.data)); } }; configStore.load(); 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.success("👁️ 进入矿洞层!上帝之眼启动!"); 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 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; 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)) if (provides[sym]?._s instanceof Map) { pinia = provides[sym]; break; } return pinia ? pinia._s.get("inventory") : null; } function autoEquipByVirtualWardrobe(currentPath) { let targetConfig = null; let targetPath = null; for (const [pathKey, config] of Object.entries(configStore.data.equipPresets)) if (currentPath.includes(pathKey)) { targetConfig = config; targetPath = pathKey; break; } if (!targetConfig || currentEquippedPath === targetPath) return; const store = getInventoryStore(); if (!store) { logger.error("无法读取背包数据库!"); return; } logger.info(`开始为地图 [${targetPath}] 换上 [${targetConfig.name}]!`); if (targetConfig.hat) { logger.info("开始穿戴帽子!"); const trueId = getDefId(targetConfig.hat); logger.info(`目标帽子ID: ${trueId}`); const idx = store.ownedHats.findIndex((h) => h.defId === trueId); if (idx !== -1 && store.equippedHatIndex !== idx) { store.equipHat(idx); logger.info(`成功穿戴帽子: ${targetConfig.hat}`); } else logger.warn(`帽子 ${targetConfig.hat} 未找到或已装备`); } if (targetConfig.weapon) { logger.info("开始穿戴武器!"); const trueId = getDefId(targetConfig.weapon); logger.info(`目标武器ID: ${trueId}`); const idx = store.ownedWeapons.findIndex((w) => w.defId === trueId); if (idx !== -1 && store.equippedWeaponIndex !== idx) { store.equipWeapon(idx); logger.info(`成功穿戴武器: ${targetConfig.weapon}`); } else logger.warn(`武器 ${targetConfig.weapon} 未找到或已装备`); } if (targetConfig.shoe) { logger.info("开始穿戴鞋子!"); const trueId = getDefId(targetConfig.shoe); logger.info(`目标鞋子ID: ${trueId}`); const idx = store.ownedShoes.findIndex((s) => s.defId === trueId); if (idx !== -1 && store.equippedShoeIndex !== idx) { store.equipShoe(idx); logger.info(`成功穿戴鞋子: ${targetConfig.shoe}`); } else logger.warn(`鞋子 ${targetConfig.shoe} 未找到或已装备`); } if (targetConfig.ring1) { logger.info("开始穿戴戒指1!"); const trueId = getDefId(targetConfig.ring1); logger.info(`目标戒指1ID: ${trueId}`); const idx = store.ownedRings.findIndex((r) => r.defId === trueId); if (idx !== -1) { store.equipRing(idx, 0); logger.info(`成功穿戴戒指1: ${targetConfig.ring1}`); } else logger.warn(`戒指1 ${targetConfig.ring1} 未找到或已装备`); } if (targetConfig.ring2) { logger.info("开始穿戴戒指2!"); const trueId = getDefId(targetConfig.ring2); logger.info(`目标戒指2ID: ${trueId}`); const idx = store.ownedRings.findIndex((r) => r.defId === trueId); if (idx !== -1) { store.equipRing(idx, 1); logger.info(`成功穿戴戒指2: ${targetConfig.ring2}`); } else logger.warn(`戒指2 ${targetConfig.ring2} 未找到或已装备`); } currentEquippedPath = targetPath; showToast(`👗 已自动换装: ${targetConfig.name}`); logger.info(`✨ [虚拟衣柜] 成功为地图 [${targetPath}] 换上 [${targetConfig.name}]!`); } 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(".ty-helper-btn{color:#3b82f6;cursor:grab;z-index:9998;background-color:#fff;border:1px solid #e5e7eb;border-radius:50%;justify-content:center;align-items:center;width:44px;height:44px;display:flex;position:fixed;top:15%;right:20px;box-shadow:0 4px 6px -1px #0000001a}.ty-helper-btn:active{cursor:grabbing}.ty-helper-panel{z-index:9999;background-color:#f8fafc;border:1px solid #e2e8f0;border-radius:12px;flex-direction:column;width:380px;height:600px;font-family:sans-serif;display:none;position:fixed;top:15%;right:80px;overflow:hidden;box-shadow:0 10px 25px -5px #0000001a}.ty-helper-panel.open{display:flex}.ty-panel-header{cursor:grab;-webkit-user-select:none;user-select:none;background-color:#fff;border-bottom:1px solid #e2e8f0;justify-content:space-between;align-items:center;padding:12px 16px;display:flex}.ty-panel-header:active{cursor:grabbing}.ty-panel-header h3{color:#1e293b;pointer-events:none;margin:0;font-size:16px}.ty-panel-close-btn{cursor:pointer;color:#94a3b8;background:0 0;border:none;font-size:18px}.ty-panel-close-btn:hover{color:#ef4444}.ty-tabs{background:#fff;border-bottom:1px solid #e2e8f0;display:flex}.ty-tab-btn{color:#64748b;cursor:pointer;background:0 0;border:none;border-bottom:2px solid #0000;flex:1;padding:10px 0;font-size:14px;transition:all .2s}.ty-tab-btn:hover{color:#3b82f6;background:#f8fafc}.ty-tab-btn.active{color:#3b82f6;border-bottom-color:#3b82f6;font-weight:700}.ty-tab-content{flex:1;padding:16px;position:relative;overflow-y:auto}.ty-tab-pane{display:none}.ty-tab-pane.active{animation:.2s fadeIn;display:block}@keyframes fadeIn{0%{opacity:0;transform:translateY(5px)}to{opacity:1;transform:translateY(0)}}.ty-config-group{background:#fff;border:1px solid #e2e8f0;border-radius:8px;margin-bottom:16px;padding:12px}.ty-config-title{color:#334155;align-items:center;gap:6px;margin-bottom:8px;font-size:14px;font-weight:700;display:flex}.ty-config-row{justify-content:space-between;align-items:center;margin-bottom:8px;display:flex}.ty-config-row label{color:#64748b;width:60px;font-size:13px}.ty-config-row select,.ty-config-row input{color:#0f172a;background-color:#f8fafc;border:1px solid #cbd5e1;border-radius:4px;outline:none;flex:1;padding:4px 8px;font-size:13px;font-weight:600}.ty-config-row input:focus,.ty-config-row select:focus{background-color:#fff;border-color:#3b82f6}.ty-save-btn{color:#fff;cursor:pointer;background:#3b82f6;border:none;border-radius:6px;width:100%;margin-top:10px;padding:10px;font-weight:700}.ty-save-btn:hover{background:#2563eb}"); var layout_default = "