您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
从 Milkonomy 中复制内容,生成单子所需的原料
// ==UserScript== // @name [牛牛] Milkonomy 计算器 // @namespace http://tampermonkey.net/ // @version 1.3 // @description 从 Milkonomy 中复制内容,生成单子所需的原料 // @author jxxzs // @match https://www.milkywayidle.com/game* // @run-at document-idle // @require https://code.jquery.com/jquery-3.6.0.min.js // ==/UserScript== (async () => { // 数字转换函数 function ParseCount(str) { str = str.trim(); let num = parseFloat(str.replace(/[^0-9.]/g, '')) || 0; if (str.includes('B')) return num * 1_000_000_000; if (str.includes('M')) return num * 1_000_000; if (str.includes('K')) return num * 1_000; return num; } // 获取指定物品的数量 function GetItemCountByName(name) { const $svg = $('div.Inventory_inventory__17CH2') .find(`svg[aria-label='${name}']`).first(); return $svg.length ? ParseCount($svg.closest('div.Item_item__2De2O') .find('div.Item_count__1HVvv') .text()) : 0; } // 剪贴板转换函数 function ParseItems(inputText) { if (!inputText || !inputText.trim()) return null; const lines = inputText .split(/\r?\n/) .map(l => l.trim()) .filter(Boolean); // 使用正则检查每个物品块的格式 const validFourLine = /^.+\n\d+(\.\d+)?个\n¥?[\d,]+(\.\d+)?\n[\d,]+(\.\d+)?\s*\/\s*h$/; const validThreeLine = /^.+\n¥?[\d,]+(\.\d+)?\n[\d,]+(\.\d+)?\s*\/\s*h$/; // 将行合并成每个物品块 let i = 0; while (i < lines.length) { if (/\d+(\.\d+)?个/.test(lines[i + 1])) { // 四行格式 const block = lines.slice(i, i + 4).join('\n'); if (!validFourLine.test(block)) return null; i += 4; } else { // 三行格式 const block = lines.slice(i, i + 3).join('\n'); if (!validThreeLine.test(block)) return null; i += 3; } } const items = []; for (let i = 0; i < lines.length;) { const name = lines[i]; let priceLine, rateLine; // 判断第二行是否是 "X个" if (/\d+(\.\d+)?个/.test(lines[i + 1])) { priceLine = lines[i + 2]; rateLine = lines[i + 3]; i += 4; } else { priceLine = lines[i + 1]; rateLine = lines[i + 2]; i += 3; } const price = parseFloat(priceLine.replace(/[^\d.]/g, "")); const rate = parseFloat(rateLine.replace(/[^\d.]/g, "")); const count = GetItemCountByName(name) || 0; items.push({ name, price, rate, count }); } return items.length ? items : null; // 如果没有任何物品,也返回 null } // 等待指定元素出现 function WaitForElement(selector, $root = $(document), timeout = 30000) { return new Promise((resolve, reject) => { const interval = 100; let elapsed = 0; const timer = setInterval(() => { const element = $root.find(selector); if (element.length) { clearInterval(timer); resolve(element); } else { elapsed += interval; if (elapsed >= timeout) { clearInterval(timer); reject(new Error('元素未出现')); message('请刷新页面,重新加载脚本'); } } }, interval); }); } // 计算最多可持续小时数 function CalculateMaxHoursSegment(items, money) { const validItems = items.filter(i => i.rate > 0); const stock = validItems.map(i => i.count); // 每个物品库存支撑的时间 const timePoints = validItems.map((i, idx) => i.rate > 0 ? i.count / i.rate : Infinity); // 去重并升序 const uniqueTimes = Array.from(new Set(timePoints)).sort((a, b) => a - b); let hours = 0; let prevTime = 0; for (let t of uniqueTimes) { const dt = t - prevTime; // 当前段长度 if (dt <= 0) continue; // 计算这一段金币需求 let neededCoins = 0; for (let i = 0; i < validItems.length; i++) { const item = validItems[i]; const consume = dt * item.rate; if (stock[i] >= consume) { stock[i] -= consume; // 用库存 } else { neededCoins += (consume - stock[i]) * item.price; stock[i] = 0; // 库存耗尽 } } if (neededCoins > money) { // 金币不足,按比例计算剩余小时 hours += dt * (money / neededCoins); return hours; } money -= neededCoins; hours += dt; prevTime = t; } // 如果金币还够,剩余时间可无限制用库存或金币(可以返回 Infinity 或继续按速率计算) // 为了现实,这里再计算一小时消耗金币 const totalRatePrice = validItems.reduce((sum, i) => sum + i.rate * i.price, 0); if (totalRatePrice > 0) { hours += money / totalRatePrice; } return hours; } // 根据想做的小时数计算每个物品需要购买的数量 function CalculatePurchase(items, desiredHours) { if (!Array.isArray(items) || items.length === 0) return []; return items.map(item => { const required = desiredHours * item.rate; // 总消耗量 const needToBuy = Math.max(0, required - item.count); // 减去现有库存 return { name: item.name, quantity: Math.floor(needToBuy) }; }); } // 创建 Message 的窗口 function CreateMessageMenu() { // 创建 alert 菜单 const $alert = $('<div id="MessageMenu">').css({ position: 'fixed', bottom: '20px', right: '20px', display: 'flex', flexDirection: 'column', alignItems: 'flex-start', zIndex: 99999, backgroundColor: '#00000099', cursor: 'pointer' }); $('body').append($alert); } // 提示信息 function message(msg) { // console.log(`[右键删除] ${msg}`); const $div = $('<div>') .css({ backgroundColor: '#000000dd', color: '#ffffffff' }) .text(`[右键删除] ${msg}`); $('#MessageMenu').append($div); $div.on('contextmenu', (e) => { e.preventDefault(); $div.remove(); }); } // 等待页面加载完毕 await WaitForElement('div.NavigationBar_navigationLinks__1XSSb'); // 创建 Message 的窗口 CreateMessageMenu(); // 修改光标 const style = document.createElement('style'); style.textContent = ` div.Header_logoContainer__1sCnZ:hover { cursor: zoom-in; } `; document.head.appendChild(style); // 按钮点击返回值 $('div.Header_logoContainer__1sCnZ').click(async () => { const text = await navigator.clipboard.readText() const items = ParseItems(text); if (items === null) { alert('请检查剪贴板格式是否正确,现在是\n\n' + text); return; } const money = GetItemCountByName('金币'); const maxTime = CalculateMaxHoursSegment(items, money); const value = prompt("想做的数量(默认为最大数量):", maxTime.toFixed(2)); if (value === null) { return; } const desiredHours = parseFloat(value); if (isNaN(desiredHours) || desiredHours <= 0 || desiredHours > maxTime) { alert("请输入有效数字"); return; } const result = CalculatePurchase(items, desiredHours); result.forEach((item, index) => { message(`${item.name}: ${item.quantity}`); }); }); })();