// ==UserScript== // @name [银河奶牛]食用工具 // @namespace http://tampermonkey.net/ // @version 0.444 // @description 开箱记录、箱子期望、离线统计、公会钉钉、食物警察、掉落追踪 // @author Truth_Light // @license Truth_Light // @match https://www.milkywayidle.com/* // @match https://test.milkywayidle.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=milkywayidle.com // @grant GM.xmlHttpRequest // @grant GM_registerMenuCommand // @grant GM_openInTab // @grant GM_setValue // @grant GM_getValue // @downloadURL none // ==/UserScript== (async function() { 'use strict'; const itemSelector = '.ItemDictionary_drop__24I5f'; const iconSelector = '.Icon_icon__2LtL_ use'; const chestNameSelector = '#root > div > div > div.Modal_modalContainer__3B80m > div.Modal_modal__1Jiep > div.ItemDictionary_modalContent__WvEBY > div.ItemDictionary_itemAndDescription__28_he > div.Item_itemContainer__x7kH1 > div > div > div > svg > use'; const MARKET_API_URL = "https://raw.githubusercontent.com/holychikenz/MWIApi/main/medianmarket.json"; let marketData = null; let timer = null; let formattedChestDropData = {}; let battlePlayerFood = {}; let battlePlayerLoot = {}; let battleDuration; let item_icon_url const init_Client_Data = localStorage.getItem('initClientData'); if (!init_Client_Data) return; let init_Client_Data_; try { init_Client_Data_ = JSON.parse(init_Client_Data); } catch (error) { console.error('数据解析失败:', error); return; } if (init_Client_Data_.type !== 'init_client_data') return; const item_hrid_to_name = init_Client_Data_.itemDetailMap; for (const key in item_hrid_to_name) { if (item_hrid_to_name[key] && typeof item_hrid_to_name[key] === 'object' && item_hrid_to_name[key].name) { item_hrid_to_name[key] = item_hrid_to_name[key].name; } } const item_name_to_hrid = Object.fromEntries( Object.entries(item_hrid_to_name).map(([key, value]) => [value, key]) ); const MWITools_marketAPI_json = JSON.parse(localStorage.getItem('MWITools_marketAPI_json')); async function fetchMarketData() { return new Promise((resolve, reject) => { GM.xmlHttpRequest({ method: 'GET', url: MARKET_API_URL, responseType: 'json', timeout: 5000, onload: function(response) { if (response.status === 200) { const data = JSON.parse(response.responseText); console.log('从API获取到的数据:', data); resolve(data); } else { console.error('获取数据失败。状态码:', response.status); reject(new Error('数据获取失败')); } }, ontimeout: function() { console.error('请求超时:超过5秒未能获取到数据'); reject(new Error('请求超时')); }, onerror: function(error) { console.error('获取数据时发生错误:', error); reject(error); } }); }); } try { // 尝试从 API 获取数据 marketData = await fetchMarketData(); } catch (error) { console.error('从 API 获取数据失败,尝试从本地存储获取数据。', error); // 从本地存储获取数据 const marketDataStr = localStorage.getItem('MWITools_marketAPI_json'); marketData = JSON.parse(marketDataStr); if (!marketData) { alert('无法获取 market 数据'); } else { console.log('从本地存储获取到的数据:', marketData); } } function getSpecialItemPrice(itemName, priceType) { if (marketData?.market?.[itemName]) { const itemPrice = marketData.market[itemName][priceType]; if (itemPrice !== undefined && itemPrice !== -1) { return itemPrice; } } if (MWITools_marketAPI_json?.market?.[itemName]) { const itemPrice = MWITools_marketAPI_json.market[itemName][priceType]; if (itemPrice !== undefined && itemPrice !== -1) { return itemPrice; } } console.error(`未找到物品 ${itemName} 的 ${priceType} 价格信息`); return null; } let specialItemPrices = { 'Coin': { ask: 1, bid: 1 }, // 默认的特殊物品价值,包括 ask 和 bid 价值 'Cowbell': { ask: getSpecialItemPrice('Bag Of 10 Cowbells', 'ask') / 10 || 50000, bid: getSpecialItemPrice('Bag Of 10 Cowbells', 'bid') / 10 || 50000 }, 'Chimerical Token': { ask: getSpecialItemPrice('Chimerical Essence', 'ask') || 3000, bid: getSpecialItemPrice('Chimerical Essence', 'bid') || 2850 }, 'Sinister Token': { ask: getSpecialItemPrice('Sinister Essence', 'ask') || 3000, bid: getSpecialItemPrice('Sinister Essence', 'bid') || 2900 }, 'Enchanted Token': { ask: getSpecialItemPrice('Enchanted Essence', 'ask') || 4000, bid: getSpecialItemPrice('Enchanted Essence', 'bid') || 4000 }, }; function getItemNameFromElement(element) { const itemNameRaw = element.getAttribute('href').split('#').pop(); return formatItemName(itemNameRaw); } function formatItemName(itemNameRaw) { return item_hrid_to_name[`/items/${itemNameRaw}`] } function formatPrice(value) { const isNegative = value < 0; value = Math.abs(value); if (value >= 1e13) { return (isNegative ? '-' : '') + (value / 1e12).toFixed(1) + 'T'; } else if (value >= 1e10) { return (isNegative ? '-' : '') + (value / 1e9).toFixed(1) + 'B'; } else if (value >= 1e7) { return (isNegative ? '-' : '') + (value / 1e6).toFixed(1) + 'M'; } else if (value >= 1e4) { return (isNegative ? '-' : '') + (value / 1e3).toFixed(1) + 'K'; } else { return (isNegative ? '-' : '') + value.toFixed(0); } } function parseQuantityString(quantityStr) { const suffix = quantityStr.slice(-1); const base = parseFloat(quantityStr.slice(0, -1)); if (suffix === 'K') { return base * 1000; } else if (suffix === 'M') { return base * 1000000; } else if (suffix === 'B') { return base * 1000000000; } else { return parseFloat(quantityStr); } } function recordChestOpening(modalElement) { if (document.querySelector('.ChestStatistics')) { return; } // 从本地存储读取数据 let edibleTools = JSON.parse(localStorage.getItem('Edible_Tools')) || {}; edibleTools.Chest_Open_Data = edibleTools.Chest_Open_Data || {}; let chestOpenData = edibleTools.Chest_Open_Data; const chestDropData = edibleTools.Chest_Drop_Data; const chestNameElement = modalElement.querySelector("div.Modal_modal__1Jiep > div.Inventory_modalContent__3ObSx > div.Item_itemContainer__x7kH1 > div > div > div.Item_iconContainer__5z7j4 > svg > use"); const chestCountElement = modalElement.querySelector("div.Modal_modal__1Jiep > div.Inventory_modalContent__3ObSx > div.Item_itemContainer__x7kH1 > div > div > div.Item_count__1HVvv"); if (chestNameElement && chestCountElement) { const chestName = getItemNameFromElement(chestNameElement); chestOpenData[chestName] = chestOpenData[chestName] || {}; let chestData = chestOpenData[chestName]; const chestCount = parseQuantityString(chestCountElement.textContent.trim()); chestData["总计开箱数量"] = (chestData["总计开箱数量"] || 0) + chestCount; chestData["获得物品"] = chestData["获得物品"] || {}; const itemsContainer = modalElement.querySelector('.Inventory_gainedItems___e9t9'); const itemElements = itemsContainer.querySelectorAll('.Item_itemContainer__x7kH1'); let totalAskValue = 0; let totalBidValue = 0; itemElements.forEach(itemElement => { const itemNameElement = itemElement.querySelector('.Item_iconContainer__5z7j4 use'); const itemQuantityElement = itemElement.querySelector('.Item_count__1HVvv'); if (itemNameElement && itemQuantityElement) { const itemName = getItemNameFromElement(itemNameElement); const itemQuantity = parseQuantityString(itemQuantityElement.textContent.trim()); const itemData = chestDropData[chestName].item[itemName] || {}; const itemAskValue = itemData["出售单价"] || 0; const itemBidValue = itemData["收购单价"] || 0; const color = itemData.Color || ''; itemQuantityElement.style.color = color; const itemOpenTotalAskValue = itemAskValue * itemQuantity; const itemOpenTotalBidValue = itemBidValue * itemQuantity; chestData["获得物品"][itemName] = chestData["获得物品"][itemName] || {}; chestData["获得物品"][itemName]["数量"] = (chestData["获得物品"][itemName]["数量"] || 0) + itemQuantity; chestData["获得物品"][itemName]["总计Ask价值"] = (chestData["获得物品"][itemName]["总计Ask价值"] || 0) + itemOpenTotalAskValue; chestData["获得物品"][itemName]["总计Bid价值"] = (chestData["获得物品"][itemName]["总计Bid价值"] || 0) + itemOpenTotalBidValue; totalAskValue += itemOpenTotalAskValue; totalBidValue += itemOpenTotalBidValue; } }); chestData["总计开箱Ask"] = (chestData["总计开箱Ask"] || 0) + totalAskValue; chestData["总计开箱Bid"] = (chestData["总计开箱Bid"] || 0) + totalBidValue; // 计算本次开箱的偏差值 const differenceValue = totalBidValue - chestDropData[chestName]["期望产出Bid"] * chestCount; // 更新累计偏差值 chestData["累计偏差值"] = (chestData["累计偏差值"] || 0) + differenceValue; // 地牢开箱 let profitRange = null; let profitColor = 'lime'; // 默认颜色 const chestCosts = { "Chimerical Chest": { keyAsk: getSpecialItemPrice('Chimerical Chest Key', 'ask') || 2700e3, keyBid: getSpecialItemPrice('Chimerical Chest Key', 'bid') || 2600e3, entryAsk: getSpecialItemPrice('Chimerical Entry Key', 'ask') || 350e3, entryBid: getSpecialItemPrice('Chimerical Entry Key', 'bid') || 320e3 }, "Sinister Chest": { keyAsk: getSpecialItemPrice('Sinister Chest Key', 'ask') || 3800e3, keyBid: getSpecialItemPrice('Sinister Chest Key', 'bid') || 3500e3, entryAsk: getSpecialItemPrice('Sinister Entry Key', 'ask') || 450e3, entryBid: getSpecialItemPrice('Sinister Entry Key', 'bid') || 400e3 }, "Enchanted Chest": { keyAsk: getSpecialItemPrice('Enchanted Chest Key', 'ask') || 5600e3, keyBid: getSpecialItemPrice('Enchanted Chest Key', 'bid') || 5400e3, entryAsk: getSpecialItemPrice('Enchanted Entry Key', 'ask') || 420e3, entryBid: getSpecialItemPrice('Enchanted Entry Key', 'bid') || 400e3 } }; if (chestCosts[chestName]) { const { keyAsk, keyBid, entryAsk, entryBid } = chestCosts[chestName]; const minProfit = totalBidValue - (keyAsk + entryAsk) * chestCount; const maxProfit = totalAskValue - (keyBid + entryBid) * chestCount; profitRange = `${formatPrice(minProfit)}~${formatPrice(maxProfit)}`; if (minProfit > 0 && maxProfit > 0) { profitColor = 'lime'; } else if (minProfit < 0 && maxProfit < 0) { profitColor = 'red'; } else { profitColor = 'orange'; } } // 显示 const openChestElement = document.querySelector('.Inventory_modalContent__3ObSx'); const displayElement = document.createElement('div'); displayElement.classList.add('ChestStatistics'); displayElement.style.position = 'absolute'; displayElement.style.left = `${openChestElement.offsetLeft}px`; displayElement.style.top = `${openChestElement.offsetTop}px`; displayElement.style.fontSize = '12px'; displayElement.innerHTML = ` 总计开箱次数:
${chestData["总计开箱数量"]}
本次开箱价值:
${formatPrice(totalAskValue)}/${formatPrice(totalBidValue)}
总计开箱价值:
${formatPrice(chestData["总计开箱Ask"])}/${formatPrice(chestData["总计开箱Bid"])}
`; const expectedOutputElement = document.createElement('div'); expectedOutputElement.classList.add('ExpectedOutput'); expectedOutputElement.style.position = 'absolute'; expectedOutputElement.style.left = `${openChestElement.offsetLeft}px`; expectedOutputElement.style.bottom = `${openChestElement.offsetTop}px`; expectedOutputElement.style.fontSize = '12px'; expectedOutputElement.innerHTML = ` 预计产出价值:
${formatPrice(chestDropData[chestName]["期望产出Ask"]*chestCount)}/${formatPrice(chestDropData[chestName]["期望产出Bid"]*chestCount)}
`; const differenceOutputElement = document.createElement('div'); differenceOutputElement.classList.add('DifferenceOutput'); differenceOutputElement.style.position = 'absolute'; differenceOutputElement.style.right = `${openChestElement.offsetLeft}px`; differenceOutputElement.style.bottom = `${openChestElement.offsetTop}px`; differenceOutputElement.style.fontSize = '12px'; differenceOutputElement.style.color = differenceValue > 0 ? 'lime' : 'red'; differenceOutputElement.innerHTML = ` ${differenceValue > 0 ? '高于期望价值:' : '低于期望价值:'}
${formatPrice(Math.abs(differenceValue))}
`; // 创建并显示累计偏差值的元素 const cumulativeDifferenceElement = document.createElement('div'); cumulativeDifferenceElement.classList.add('CumulativeDifference'); cumulativeDifferenceElement.style.position = 'absolute'; cumulativeDifferenceElement.style.right = `${openChestElement.offsetLeft}px`; cumulativeDifferenceElement.style.top = `${openChestElement.offsetTop}px`; cumulativeDifferenceElement.style.fontSize = '12px'; cumulativeDifferenceElement.style.color = chestData["累计偏差值"] > 0 ? 'lime' : 'red'; cumulativeDifferenceElement.innerHTML = `

本次开箱利润
${profitRange ? `${profitRange}` : `${formatPrice(totalAskValue)}/${formatPrice(totalBidValue)}`}
累计${chestData["累计偏差值"] > 0 ? '高于期望:' : '低于期望:'}
${formatPrice(Math.abs(chestData["累计偏差值"]))}
`; openChestElement.appendChild(displayElement); openChestElement.appendChild(expectedOutputElement); openChestElement.appendChild(differenceOutputElement); openChestElement.appendChild(cumulativeDifferenceElement); // 保存更新的数据到本地存储 localStorage.setItem('Edible_Tools', JSON.stringify(edibleTools)); } } function calculateTotalValues(itemElements) { let totalAskValue = 0; let totalBidValue = 0; itemElements.forEach(itemElement => { const itemNameElement = itemElement.querySelector('.Item_iconContainer__5z7j4 use'); const itemQuantityElement = itemElement.querySelector('.Item_count__1HVvv'); if (itemNameElement && itemQuantityElement) { const itemName = getItemNameFromElement(itemNameElement); const itemQuantity = parseQuantityString(itemQuantityElement.textContent.trim()); let askPrice = 0; let bidPrice = 0; let priceColor = ''; // 获取价格 if (specialItemPrices[itemName] && specialItemPrices[itemName].ask) { askPrice = parseFloat(specialItemPrices[itemName].ask); bidPrice = parseFloat(specialItemPrices[itemName].bid); priceColor = ''; } else if (marketData?.market?.[itemName]) { bidPrice = marketData.market[itemName].bid; askPrice = marketData.market[itemName].ask; } else if (MWITools_marketAPI_json?.market?.[itemName]) { bidPrice = MWITools_marketAPI_json.market[itemName].bid; askPrice = MWITools_marketAPI_json.market[itemName].ask; } else { console.log(`${itemName} 的价格未找到`); } const itemTotalAskValue = askPrice * itemQuantity; const itemTotalBidValue = bidPrice * itemQuantity; totalAskValue += itemTotalAskValue; totalBidValue += itemTotalBidValue; } }); //console.log(totalAskValue); return { totalAskValue, totalBidValue }; } //更详细的战斗等级显示 const updateCombatLevel = () => { const elements = document.querySelectorAll(".NavigationBar_currentExperience__3GDeX"); if (elements.length === 17) { const levels = Array.from(elements).slice(10, 17).map(el => { const levelText = parseInt(el.parentNode.parentNode.querySelector(".NavigationBar_textContainer__7TdaI .NavigationBar_level__3C7eR").textContent); const decimalPart = parseFloat(el.style.width) / 100; return { integerPart: levelText, decimalPart: decimalPart }; }); let [endurance, intelligence, attack, strength, defense, ranged, magic] = levels; let combatTypeMax = Math.max( 0.5 * (attack.integerPart + strength.integerPart), ranged.integerPart, magic.integerPart ); if (combatTypeMax !== 0.5 * (attack.integerPart + strength.integerPart)) { attack.decimalPart = 0; strength.decimalPart = 0; } if (combatTypeMax !== ranged.integerPart) ranged.decimalPart = 0; if (combatTypeMax !== magic.integerPart) magic.decimalPart = 0; let combatLevel = 0.2 * (endurance.integerPart + intelligence.integerPart + defense.integerPart) + 0.4 * combatTypeMax; combatLevel = parseFloat(combatLevel.toFixed(2)); //console.log("combatLevel",combatLevel) const integerPart = Math.floor(combatLevel); const decimalPart = combatLevel - integerPart; //console.log("integerPart",integerPart) const list1 = [ endurance.decimalPart * 0.2, intelligence.decimalPart * 0.2, attack.decimalPart * 0.2, strength.decimalPart * 0.2, defense.decimalPart * 0.2, ranged.decimalPart * 0.2, magic.decimalPart * 0.2 ]; const list2 = [ endurance.decimalPart * 0.2, intelligence.decimalPart * 0.2, attack.decimalPart * 0.2, strength.decimalPart * 0.2, defense.decimalPart * 0.2, ranged.decimalPart * 0.2, magic.decimalPart * 0.2, ranged.decimalPart * 0.2, magic.decimalPart * 0.2 ]; //console.log("list1",list1,"\nlist2",list2) list1.sort((a, b) => b - a); list2.sort((a, b) => b - a); if (decimalPart === 0.8) { combatLevel += list1[0]; } else { let total = 0; const maxIterations = Math.floor((1 - decimalPart) / 0.2); let iterations = 0; for (const i of list2) { if (iterations >= maxIterations) break; if ((decimalPart + total + i) < 1) { total += i; } else { break; } iterations++; } combatLevel = decimalPart + integerPart + total; } elements[15].parentNode.parentNode.parentNode.parentNode.parentNode.querySelector(".NavigationBar_nav__3uuUl .NavigationBar_level__3C7eR").textContent = combatLevel.toFixed(2); } }; window.setInterval(updateCombatLevel, 10000); function OfflineStatistics(modalElement) { const itemsContainer = modalElement.querySelectorAll(".OfflineProgressModal_itemList__26h-Y"); let timeContainer = null; let getItemContainer = null; let spendItemContainer = null; itemsContainer.forEach(container => { const labelElement = container.querySelector('.OfflineProgressModal_label__2HwFG'); if (labelElement) { const textContent = labelElement.textContent.trim(); if (textContent.startsWith("You were offline for") || textContent.startsWith("你离线了")) { timeContainer = container; } else if (textContent.startsWith("Items gained:") || textContent.startsWith("获得物品:")) { getItemContainer = container; } else if (textContent.startsWith("You consumed:") || textContent.startsWith("你消耗了:")) { spendItemContainer = container; } } }); let TotalSec = null; if (timeContainer) { const textContent = timeContainer.textContent; const match = textContent.match(/(?:(\d+)d\s*)?(?:(\d+)h\s*)?(?:(\d+)m\s*)?(?:(\d+)s)/); if (match) { let days = parseInt(match[1], 10) || 0; let hours = parseInt(match[2], 10) || 0; let minutes = parseInt(match[3], 10) || 0; let seconds = parseInt(match[4], 10) || 0; TotalSec = days * 86400 + hours * 3600 + minutes * 60 + seconds; } } let getitemtotalAskValue = 0; let getitemtotalBidValue = 0; if (getItemContainer) { const getitemElements = getItemContainer.querySelectorAll('.Item_itemContainer__x7kH1'); const { totalAskValue, totalBidValue } = calculateTotalValues(getitemElements); getitemtotalAskValue = totalAskValue; getitemtotalBidValue = totalBidValue; } let spenditemtotalAskValue = 0; let spenditemtotalBidValue = 0; if (spendItemContainer) { const spenditemElements = spendItemContainer.querySelectorAll('.Item_itemContainer__x7kH1'); const { totalAskValue, totalBidValue } = calculateTotalValues(spenditemElements); spenditemtotalAskValue = totalAskValue; spenditemtotalBidValue = totalBidValue; } if (timeContainer) { const newElement = document.createElement('span'); newElement.textContent = `利润: ${formatPrice(getitemtotalBidValue - spenditemtotalAskValue)} [${formatPrice((getitemtotalBidValue - spenditemtotalAskValue) / (TotalSec / 3600) * 24)}/天]`; newElement.style.float = 'right'; newElement.style.color = 'gold'; timeContainer.querySelector(':first-child').appendChild(newElement); } if (getItemContainer) { const newElement = document.createElement('span'); newElement.textContent = `产出:[${formatPrice(getitemtotalAskValue)}/${formatPrice(getitemtotalBidValue)}]`; newElement.style.float = 'right'; newElement.style.color = 'gold'; getItemContainer.querySelector(':first-child').appendChild(newElement); } if (spendItemContainer) { const newElement = document.createElement('span'); newElement.textContent = `成本:[${formatPrice(spenditemtotalAskValue)}/${formatPrice(spenditemtotalBidValue)}]`; newElement.style.float = 'right'; newElement.style.color = 'gold'; spendItemContainer.querySelector(':first-child').appendChild(newElement); } } function initObserver() { // 选择要观察的目标节点 const targetNode = document.body; // 观察器的配置(需要观察子节点的变化) const config = { childList: true, subtree: true }; // 创建一个观察器实例并传入回调函数 const observer = new MutationObserver(mutationsList => { for (let mutation of mutationsList) { if (mutation.type === 'childList') { // 监听到子节点变化 mutation.addedNodes.forEach(addedNode => { // 检查是否是我们关注的 Modal_modalContainer__3B80m 元素被添加 if (addedNode.classList && addedNode.classList.contains('Modal_modalContainer__3B80m')) { // Modal_modalContainer__3B80m 元素被添加,执行处理函数 //console.log("箱子被打开") ShowChestPrice(); recordChestOpening(addedNode); // 开始监听箱子图标的变化 startIconObserver(); } if (addedNode.classList && addedNode.classList.contains('OfflineProgressModal_modalContainer__knnk7')) { OfflineStatistics(addedNode); console.log("离线报告已创建!") } if (addedNode.classList && addedNode.classList.contains('MainPanel_subPanelContainer__1i-H9')) { if (addedNode.querySelector(".CombatPanel_combatPanel__QylPo")) { addBattlePlayerFoodButton(); addBattlePlayerLootButton(); } } }); mutation.removedNodes.forEach(removedNode => { // 检查是否是 Modal_modalContainer__3B80m 元素被移除 if (removedNode.classList && removedNode.classList.contains('Modal_modalContainer__3B80m')) { // Modal_modalContainer__3B80m 元素被移除,停止监听箱子图标的变化 stopIconObserver(); } }); } } }); // 以上述配置开始观察目标节点 observer.observe(targetNode, config); // 定义箱子图标变化的观察器 let iconObserver = null; // 开始监听箱子图标的变化 function startIconObserver() { const chestNameElem = document.querySelector(chestNameSelector); if (!chestNameElem) return; // 创建一个观察器实例来监听图标的变化 iconObserver = new MutationObserver(() => { // 当箱子图标变化时,执行处理函数 ShowChestPrice(); }); // 配置观察器的选项 const iconConfig = { attributes: true, attributeFilter: ['href'] }; // 以上述配置开始观察箱子图标节点 iconObserver.observe(chestNameElem, iconConfig); } // 停止监听箱子图标的变化 function stopIconObserver() { if (iconObserver) { iconObserver.disconnect(); iconObserver = null; } } } initObserver(); //公会部分代码 const userLanguage = navigator.language || navigator.userLanguage; const isZH = userLanguage.startsWith("zh"); const updataDealy = 24*60*60*1000; //数据更新时限 let rateXPDayMap = {}; function hookWS() { const dataProperty = Object.getOwnPropertyDescriptor(MessageEvent.prototype, "data"); const oriGet = dataProperty.get; dataProperty.get = hookedGet; Object.defineProperty(MessageEvent.prototype, "data", dataProperty); function hookedGet() { const socket = this.currentTarget; if (!(socket instanceof WebSocket)) { return oriGet.call(this); } if (socket.url.indexOf("api.milkywayidle.com/ws") <= -1 && socket.url.indexOf("api-test.milkywayidle.com/ws") <= -1) { return oriGet.call(this); } const message = oriGet.call(this); Object.defineProperty(this, "data", { value: message }); // Anti-loop return handleMessage(message); } } function addStatisticsButton() { const waitForNavi = () => { const targetNode = document.querySelector("div.NavigationBar_minorNavigationLinks__dbxh7"); // 确认这个选择器是否适合你的环境 if (targetNode) { // 创建统计窗口按钮 let statsButton = document.createElement("div"); statsButton.setAttribute("class", "NavigationBar_minorNavigationLink__31K7Y"); statsButton.style.color = "gold"; statsButton.innerHTML = isZH ? "开箱统计" : "Chest Statistics"; statsButton.addEventListener("click", () => { const edibleTools = JSON.parse(localStorage.getItem('Edible_Tools')) || {}; const openChestData = edibleTools.Chest_Open_Data || {}; createVisualizationWindow(openChestData); }); // 创建食用工具按钮 let edibleToolsButton = document.createElement("div"); edibleToolsButton.setAttribute("class", "NavigationBar_minorNavigationLink__31K7Y"); edibleToolsButton.style.color = "gold"; edibleToolsButton.innerHTML = "食用工具"; edibleToolsButton.addEventListener("click", () => { openSettings(); }); // 将按钮添加到目标节点 targetNode.insertAdjacentElement("afterbegin", statsButton); targetNode.insertAdjacentElement("afterbegin", edibleToolsButton); //获取图标url格式模板 item_icon_url = document.querySelector("div[class^='Item_itemContainer'] use")?.getAttribute("href")?.split("#")[0]; } else { setTimeout(waitForNavi, 200); } }; waitForNavi(); // 开始等待目标节点出现 } //奶牛钉钉 function handleMessage(message) { try { let obj = JSON.parse(message); if (obj && obj.type === "new_battle") { processCombatConsumables(obj); } if (obj && obj.type === "init_character_data") { processAndPrintData(); addStatisticsButton(); update_market_list(obj); } if (obj && obj.type === "guild_updated") { const Guild_ID = obj.guild.id; const edibleTools = JSON.parse(localStorage.getItem('Edible_Tools')) || {}; edibleTools.Guild_Data = edibleTools.Guild_Data || {}; let storedData = edibleTools.Guild_Data || {}; // 判断是否已经存在旧数据 if (storedData[Guild_ID] && storedData[Guild_ID].guild_updated && storedData[Guild_ID].guild_updated.old.updatedAt) { const oldUpdatedAt = new Date(storedData[Guild_ID].guild_updated.new.updatedAt); const newUpdatedAt = new Date(obj.guild.updatedAt); // 计算时间差(单位:毫秒) const timeDifference = newUpdatedAt - oldUpdatedAt; if (timeDifference >= updataDealy) { // 更新老数据为新数据 storedData[Guild_ID].guild_updated.old = storedData[Guild_ID].guild_updated.new; // 更新新数据为当前数据 storedData[Guild_ID].guild_updated.new = { experience: obj.guild.experience, level: obj.guild.level, updatedAt: obj.guild.updatedAt }; } else { // 仅更新新数据 storedData[Guild_ID].guild_updated.new = { experience: obj.guild.experience, level: obj.guild.level, updatedAt: obj.guild.updatedAt }; } //计算Δ const Delta = { Delta_Xp: storedData[Guild_ID].guild_updated.new.experience - storedData[Guild_ID].guild_updated.old.experience, Delta_Level: storedData[Guild_ID].guild_updated.new.level - storedData[Guild_ID].guild_updated.old.level, Delta_Time: (newUpdatedAt - new Date(storedData[Guild_ID].guild_updated.old.updatedAt)) / 1000, // 转换为秒 Rate_XP_Hours: (3600*(obj.guild.experience - storedData[Guild_ID].guild_updated.old.experience)/((newUpdatedAt - new Date(storedData[Guild_ID].guild_updated.old.updatedAt)) / 1000)).toFixed(2) }; storedData[Guild_ID].guild_updated.Delta = Delta; const Guild_TotalXp_div = document.querySelectorAll(".GuildPanel_value__Hm2I9")[1]; if (Guild_TotalXp_div) { const xpText = isZH ? "经验值 / 小时" : "XP / Hour"; Guild_TotalXp_div.insertAdjacentHTML( "afterend", `
${formatPrice(Delta.Rate_XP_Hours)} ${xpText}
` ); const Guild_NeedXp_div = document.querySelectorAll(".GuildPanel_value__Hm2I9")[2]; if (Guild_NeedXp_div) { const Guild_NeedXp = document.querySelectorAll(".GuildPanel_value__Hm2I9")[2].textContent.replace(/,/g, ''); const Time = TimeReset(Guild_NeedXp/Delta.Rate_XP_Hours); Guild_NeedXp_div.insertAdjacentHTML( "afterend", // 使用 "afterend" 在元素的后面插入内容 `
${Time}
` ); } } } else { // 如果没有旧数据,则直接添加新数据 storedData[Guild_ID] = { guild_name: obj.guild.name, guild_updated: { old: { experience: obj.guild.experience, level: obj.guild.level, updatedAt: obj.guild.updatedAt }, new: {}, } }; } // 存储更新后的数据到 localStorage edibleTools.Guild_Data = storedData; localStorage.setItem('Edible_Tools', JSON.stringify(edibleTools)); } else if (obj && obj.type === "guild_characters_updated") { const edibleTools = JSON.parse(localStorage.getItem('Edible_Tools')) || {}; edibleTools.Guild_Data = edibleTools.Guild_Data || {}; let storedData = edibleTools.Guild_Data || {}; for (const key in obj.guildSharableCharacterMap) { if (obj.guildSharableCharacterMap.hasOwnProperty(key)) { const Guild_ID = obj.guildCharacterMap[key].guildID; const name = obj.guildSharableCharacterMap[key].name; const newUpdatedAt = new Date(); storedData[Guild_ID].guild_player = storedData[Guild_ID].guild_player || {}; if (storedData[Guild_ID] && storedData[Guild_ID].guild_player && storedData[Guild_ID].guild_player[name] && storedData[Guild_ID].guild_player[name].old && storedData[Guild_ID].guild_player[name].old.updatedAt) { const oldUpdatedAt = new Date(storedData[Guild_ID].guild_player[name].old.updatedAt) const timeDifference = newUpdatedAt - oldUpdatedAt if (timeDifference >= updataDealy) { // 更新老数据为新数据 storedData[Guild_ID].guild_player[name].old = storedData[Guild_ID].guild_player[name].new; // 更新新数据为当前数据 storedData[Guild_ID].guild_player[name].new = { id: key, gameMode: obj.guildSharableCharacterMap[key].gameMode, guildExperience: obj.guildCharacterMap[key].guildExperience, updatedAt: newUpdatedAt, }; } else { // 仅更新新数据 storedData[Guild_ID].guild_player[name].new = { id: key, gameMode: obj.guildSharableCharacterMap[key].gameMode, guildExperience: obj.guildCharacterMap[key].guildExperience, updatedAt: newUpdatedAt, }; } //计算Δ const Delta = { Delta_Time:(newUpdatedAt - new Date(storedData[Guild_ID].guild_player[name].old.updatedAt)) / 1000, Delta_Xp: storedData[Guild_ID].guild_player[name].new.guildExperience - storedData[Guild_ID].guild_player[name].old.guildExperience, Rate_XP_Day: (24*3600*(obj.guildCharacterMap[key].guildExperience - storedData[Guild_ID].guild_player[name].old.guildExperience)/((newUpdatedAt - new Date(storedData[Guild_ID].guild_player[name].old.updatedAt)) / 1000)).toFixed(2) }; storedData[Guild_ID].guild_player[name].Delta = Delta; rateXPDayMap[name] = Delta.Rate_XP_Day; }else { storedData[Guild_ID].guild_player[name] = { old: { id: key, gameMode: obj.guildSharableCharacterMap[key].gameMode, guildExperience: obj.guildCharacterMap[key].guildExperience, updatedAt: newUpdatedAt, }, new:{} }; } } } //console.log("测试数据",storedData); //console.log("guild_characters_updated", obj); updateExperienceDisplay(rateXPDayMap); edibleTools.Guild_Data = storedData; localStorage.setItem('Edible_Tools', JSON.stringify(edibleTools)); } else if (obj && obj.type === "market_listings_updated") { update_market_list(obj); } } catch (error) { console.error("Error processing message:", error); } return message; } // 订单数据更新 function update_market_list(date) { if (!date) return; let market_list = JSON.parse(GM_getValue('market_list', '[]')); // 通用更新 function updateOrders(orders) { orders.forEach(newOrder => { const existingOrderIndex = market_list.findIndex(order => order.id === newOrder.id); if (existingOrderIndex !== -1) { market_list[existingOrderIndex] = newOrder; } else { market_list.push(newOrder); } // 给每个订单添加更新时间戳 newOrder.lastUpdated = new Date().toISOString(); }); } // 更新市场数据 if (date.type === "init_character_data" && date.myMarketListings) { updateOrders(date.myMarketListings); } else if (date.type === "market_listings_updated" && date.endMarketListings) { updateOrders(date.endMarketListings); } // 保存更新后的数据 GM_setValue('market_list', JSON.stringify(market_list)); } function deleteOrdersBeforeDate() { const userInput = prompt("请输入要删除之前的日期 (格式:YYYY-MM-DD)", ""); if (!userInput) return; // 转换用户输入的日期为 Date 对象 const userDate = new Date(userInput); if (isNaN(userDate)) { alert("无效的日期格式,请使用 YYYY-MM-DD"); return; } let market_list = JSON.parse(GM_getValue('market_list', '[]')); // 过滤出所有在用户选择日期之前的订单 const filteredMarketList = market_list.filter(order => { const orderDate = new Date(order.lastUpdated); return orderDate >= userDate; }); // 更新并保存新的数据 GM_setValue('market_list', JSON.stringify(filteredMarketList)); alert("删除成功,已清理日期之前的数据。"); } function TimeReset(hours) { const totalMinutes = hours * 60; const days = Math.floor(totalMinutes / (24 * 60)); const yudays = totalMinutes % (24 * 60); const hrs = Math.floor(yudays / 60); const minutes = Math.floor(yudays % 60); const dtext = isZH ? "天" : "d"; const htext = isZH ? "时" : "h"; const mtext = isZH ? "分" : "m"; return `${days}${dtext} ${hrs}${htext} ${minutes}${mtext}`; } function updateExperienceDisplay(rateXPDayMap) { const trElements = document.querySelectorAll(".GuildPanel_membersTable__1NwIX tbody tr"); const idleuser_list = []; const dtext = isZH ? "天" : "d"; // 将 rateXPDayMap 转换为数组并排序 const sortedMembers = Object.entries(rateXPDayMap) .map(([name, XPdata]) => ({ name, XPdata })) .sort((a, b) => b.XPdata - a.XPdata); sortedMembers.forEach(({ name, XPdata }) => { trElements.forEach(tr => { const nameElement = tr.querySelector(".CharacterName_name__1amXp"); const experienceElement = tr.querySelector("td:nth-child(3) > div"); const activityElement = tr.querySelector('.GuildPanel_activity__9vshh'); if (nameElement && nameElement.textContent.trim() === name) { if (activityElement.childElementCount === 0) { idleuser_list.push(nameElement.textContent.trim()); } if (experienceElement) { const newDiv = document.createElement('div'); newDiv.textContent = `${formatPrice(XPdata)}/${dtext}`; // 计算颜色 const rank = sortedMembers.findIndex(member => member.name === name); const hue = 120 - (rank * (120 / (sortedMembers.length - 1))); newDiv.style.color = `hsl(${hue}, 100%, 50%)`; experienceElement.insertAdjacentElement('afterend', newDiv); } return; } }); }); update_idleuser_tb(idleuser_list); } function update_idleuser_tb(idleuser_list) { const targetElement = document.querySelector('.GuildPanel_noticeMessage__3Txji'); if (!targetElement) { console.error('公会标语元素未找到!'); return; } const clonedElement = targetElement.cloneNode(true); const namesText = idleuser_list.join(', '); clonedElement.innerHTML = ''; clonedElement.textContent = isZH ? `闲置的成员:${namesText}` : `Idle User : ${namesText}`; clonedElement.style.color = '#ffcc00'; // 设置复制元素的高度为原元素的25% const originalStyle = window.getComputedStyle(targetElement); const originalHeight = originalStyle.height; const originalMinHeight = originalStyle.minHeight; clonedElement.style.height = `25%`; clonedElement.style.minHeight = `25%`; // 也设置最小高度 targetElement.parentElement.appendChild(clonedElement); } hookWS(); //箱子数据获取 function processAndPrintData() { const initClientData = localStorage.getItem('initClientData'); if (!initClientData) return; let obj; try { obj = JSON.parse(initClientData); } catch (error) { console.error('数据解析失败:', error); return; } if (obj.type !== 'init_client_data') return; const item_hrid_to_name = obj.itemDetailMap; for (const key in item_hrid_to_name) { if (item_hrid_to_name[key] && typeof item_hrid_to_name[key] === 'object' && item_hrid_to_name[key].name) { item_hrid_to_name[key] = item_hrid_to_name[key].name; } } const item_name_to_hrid = Object.fromEntries( Object.entries(item_hrid_to_name).map(([key, value]) => [value, key]) ); const hrid2name = item_hrid_to_name; let formattedShopData = {}; // 处理商店数据 for (let [key, details] of Object.entries(obj.shopItemDetailMap)) { const { itemHrid, costs } = details; const itemName = hrid2name[itemHrid] || formatItemName(itemHrid.split('/').pop()); costs.forEach(cost => { const costItemName = hrid2name[cost.itemHrid] || formatItemName(cost.itemHrid.split('/').pop()); if (costItemName === "Coin") return; const costCount = cost.count; if (!formattedShopData[costItemName]) { formattedShopData[costItemName] = { items: {}, 最挣钱: '', BID单价: 0 }; } // 计算每种代币购买每个物品的收益 let bidValue = getSpecialItemPrice(itemName,"bid") || 0; let profit = bidValue / costCount; formattedShopData[costItemName].items[itemName] = { 花费: costCount }; // 更新最赚钱的物品信息 if (profit > formattedShopData[costItemName].BID单价) { formattedShopData[costItemName].最挣钱 = itemName; formattedShopData[costItemName].BID单价 = profit; specialItemPrices[costItemName].ask = profit; specialItemPrices[costItemName].bid = profit; } }); } const mostProfitableItems = Object.values(formattedShopData).map(item => item.最挣钱).filter(Boolean); //console.log(mostProfitableItems) // 处理箱子掉落物数据 for (let iteration = 0; iteration < 4; iteration++) { for (let [key, items] of Object.entries(obj.openableLootDropMap)) { const boxName = hrid2name[key] || formatItemName(key.split('/').pop()); if (!formattedChestDropData[boxName]) { formattedChestDropData[boxName] = { item: {} }; } let TotalAsk = 0; let TotalBid = 0; let awa = 0; items.forEach(item => { const { itemHrid, dropRate, minCount, maxCount } = item; const itemName = hrid2name[itemHrid] || formatItemName(itemHrid.split('/').pop()); const expectedYield = ((minCount + maxCount) / 2) * dropRate; let bidPrice = -1; let askPrice = -1; let priceColor = ''; if (specialItemPrices[itemName] && specialItemPrices[itemName].ask) { askPrice = parseFloat(specialItemPrices[itemName].ask); bidPrice = parseFloat(specialItemPrices[itemName].bid); priceColor = ''; } else if (marketData?.market?.[itemName]) { bidPrice = marketData.market[itemName].bid; askPrice = marketData.market[itemName].ask; } else if (MWITools_marketAPI_json?.market?.[itemName]) { bidPrice = MWITools_marketAPI_json.market[itemName].bid; askPrice = MWITools_marketAPI_json.market[itemName].ask; } else { console.log(`${itemName} 的价格未找到`); } if (formattedChestDropData[boxName].item[itemName] && iteration === 0) { // 如果物品已存在,更新期望掉落和相关价格 const existingItem = formattedChestDropData[boxName].item[itemName]; existingItem.期望掉落 += expectedYield; } else if (iteration === 0) { formattedChestDropData[boxName].item[itemName] = { 期望掉落: expectedYield, }; } // 判断 itemName 是否在最挣钱物品列表中 if (mostProfitableItems.includes(itemName)) { priceColor = '#FFb3E6'; } else if (askPrice === -1 && bidPrice === -1) { priceColor = 'yellow'; } else if (askPrice === -1) { askPrice = bidPrice; priceColor = '#D95961'; } else if (bidPrice === -1) { priceColor = '#2FC4A7'; } const existingItem = formattedChestDropData[boxName].item[itemName]; existingItem.出售单价 = askPrice; existingItem.收购单价 = bidPrice; existingItem.出售总价 = (existingItem.出售单价 * existingItem.期望掉落).toFixed(2); existingItem.收购总价 = (existingItem.收购单价 * existingItem.期望掉落).toFixed(2); existingItem.Color = priceColor; // 累计总价 TotalAsk += (askPrice * expectedYield); TotalBid += (bidPrice * expectedYield); }); formattedChestDropData[boxName] = { ...formattedChestDropData[boxName], 期望产出Ask: TotalAsk.toFixed(2), 期望产出Bid: TotalBid.toFixed(2), }; if (!specialItemPrices[boxName]) { specialItemPrices[boxName] = {} } specialItemPrices[boxName].ask = formattedChestDropData[boxName].期望产出Ask; specialItemPrices[boxName].bid = formattedChestDropData[boxName].期望产出Bid; } } for (let itemName in specialItemPrices) { if (specialItemPrices.hasOwnProperty(itemName)) { marketData.market[itemName] = { ask: specialItemPrices[itemName].ask, bid: specialItemPrices[itemName].bid }; } } localStorage.setItem('MWITools_marketAPI_json', JSON.stringify(marketData)); let edibleTools = JSON.parse(localStorage.getItem('Edible_Tools')) || {}; edibleTools.Chest_Drop_Data = formattedChestDropData; localStorage.setItem('Edible_Tools', JSON.stringify(edibleTools)); // 打印结果 //console.log("特殊物品价格表:",specialItemPrices) //console.log("箱子掉落物列表:", formattedChestDropData); //console.log("地牢商店列表:", formattedShopData); } function ShowChestPrice() { const modalContainer = document.querySelector(".Modal_modalContainer__3B80m"); if (!modalContainer) return; // 如果不存在 Modal_modalContainer__3B80m 元素,则直接返回 const chestNameElem = document.querySelector(chestNameSelector); if (!chestNameElem) return; const chestName = getItemNameFromElement(chestNameElem); const items = document.querySelectorAll(itemSelector); const dropListContainer = document.querySelector('.ItemDictionary_openToLoot__1krnv'); if (!dropListContainer) return; // 检查 dropListContainer 是否存在 const edibleTools = JSON.parse(localStorage.getItem('Edible_Tools')) const formattedChestDropData = edibleTools.Chest_Drop_Data; items.forEach(item => { const itemName = getItemNameFromElement(item.querySelector(iconSelector)); if (!itemName) return; // 检查 itemName 是否存在 const itemData = formattedChestDropData[chestName].item[itemName]; if (!itemData) return; // 检查 itemData 是否存在 const itemColor = itemData.Color; const itemNameElem = item.querySelector('.Item_name__2C42x'); if (itemNameElem && itemColor) { itemNameElem.style.color = itemColor; } }); const askPrice = formattedChestDropData[chestName]["期望产出Ask"]; const bidPrice = formattedChestDropData[chestName]["期望产出Bid"]; if (askPrice && bidPrice) { const previousResults = dropListContainer.querySelectorAll('.resultDiv'); previousResults.forEach(result => result.remove()); const createPriceOutput = (label, price) => { const priceOutput = document.createElement('div'); priceOutput.className = 'resultDiv'; priceOutput.textContent = `${label}: ${formatPrice(price)}`; priceOutput.style.color = 'gold'; priceOutput.style.fontSize = '14px'; priceOutput.style.fontWeight = '400'; priceOutput.style.paddingTop = '10px'; return priceOutput; }; const minPriceOutput = createPriceOutput('期望产出 (最低买入价计算)', askPrice); const maxPriceOutput = createPriceOutput('期望产出 (最高收购价计算)', bidPrice); dropListContainer.appendChild(minPriceOutput); dropListContainer.appendChild(maxPriceOutput); } } function processCombatConsumables(obj) { battlePlayerFood = {}; battlePlayerLoot = {}; battleDuration = (new Date() - new Date(obj.combatStartTime))/1000 obj.players.forEach(player => { battlePlayerFood[player.character.name] = {}; battlePlayerLoot[player.character.name] = {}; let minTimeInDays = Infinity; let minItemName = ""; player.combatConsumables.forEach(consumable => { const itemname = item_hrid_to_name[consumable.itemHrid]; const timePerUnit = itemname.includes('Coffee') ? 5 : 1; const totalTimeInMinutes = consumable.count * timePerUnit; const totalTimeInDays = totalTimeInMinutes / (24 * 60); if (totalTimeInDays < minTimeInDays) { minTimeInDays = totalTimeInDays; minItemName = itemname; } battlePlayerFood[player.character.name][itemname] = { "数量": consumable.count, "剩余时间": totalTimeInDays.toFixed(1) + '天', "颜色": "Black" }; }); Object.keys(battlePlayerFood[player.character.name]).forEach(item => { if (item === minItemName) { battlePlayerFood[player.character.name][item]["颜色"] = "red"; } }); battlePlayerFood[player.character.name]['剩余时间'] = { "数量": minTimeInDays < 1 ? (minTimeInDays * 24).toFixed(1) + '小时' : minTimeInDays.toFixed(1) + '天', "颜色": "Black" }; Object.values(player.totalLootMap).forEach(Loot => { const itemname = item_hrid_to_name[Loot.itemHrid]; battlePlayerLoot[player.character.name][itemname] = { "数量":Loot.count, "ID":Loot.itemHrid } }); }); } function addBattlePlayerFoodButton() { //出警按钮父元素路径 var tabsContainer = document.querySelector("#root > div > div > div.GamePage_gamePanel__3uNKN > div.GamePage_contentPanel__Zx4FH > div.GamePage_middlePanel__uDts7 > div.GamePage_mainPanel__2njyb > div > div:nth-child(1) > div > div > div > div.TabsComponent_tabsContainer__3BDUp > div > div > div") var referenceTab = tabsContainer ? tabsContainer.children[1] : null; if (!tabsContainer || !referenceTab) { return; } if (tabsContainer.querySelector('.Button_battlePlayerFood__custom')) { console.log('按钮已存在'); return; } var battlePlayerFoodButton = document.createElement('div'); battlePlayerFoodButton.className = referenceTab.className + ' Button_battlePlayerFood__custom'; battlePlayerFoodButton.setAttribute('script_translatedfrom', 'New Action'); battlePlayerFoodButton.textContent = '出警'; battlePlayerFoodButton.addEventListener('click', function() { let dataHtml = '
'; for (let player in battlePlayerFood) { dataHtml += `

${player}

`; let consumables = battlePlayerFood[player]; for (let item in consumables) { dataHtml += `

${item}: ${consumables[item].数量}

`; } dataHtml += '
'; } dataHtml += '
'; let popup = document.createElement('div'); popup.style.position = 'fixed'; popup.style.top = '50%'; popup.style.left = '50%'; popup.style.transform = 'translate(-50%, -50%)'; popup.style.backgroundColor = 'white'; popup.style.border = '1px solid black'; popup.style.padding = '10px'; popup.style.zIndex = '10000'; popup.style.maxWidth = '75%'; popup.style.overflowX = 'auto'; popup.style.whiteSpace = 'nowrap'; popup.innerHTML = dataHtml; let closeButton = document.createElement('button'); closeButton.textContent = '关闭'; closeButton.style.display = 'block'; closeButton.style.margin = '20px auto 0 auto'; closeButton.onclick = function() { document.body.removeChild(popup); }; popup.appendChild(closeButton); document.body.appendChild(popup); }); var lastTab = tabsContainer.children[tabsContainer.children.length - 1]; tabsContainer.insertBefore(battlePlayerFoodButton, lastTab.nextSibling); var style = document.createElement('style'); style.innerHTML = ` .Button_battlePlayerFood__custom { background-color: #546ddb; color: white; } `; document.head.appendChild(style); } function addBattlePlayerLootButton() { // 获取按钮父元素路径 var tabsContainer = document.querySelector("#root > div > div > div.GamePage_gamePanel__3uNKN > div.GamePage_contentPanel__Zx4FH > div.GamePage_middlePanel__uDts7 > div.GamePage_mainPanel__2njyb > div > div:nth-child(1) > div > div > div > div.TabsComponent_tabsContainer__3BDUp > div > div > div"); var referenceTab = tabsContainer ? tabsContainer.children[1] : null; if (!tabsContainer || !referenceTab) { return; } // 如果按钮已经存在,直接返回 if (tabsContainer.querySelector('.Button_battlePlayerLoot__custom')) { console.log('分赃按钮已存在'); return; } // 创建按钮 var battlePlayerLootButton = document.createElement('div'); battlePlayerLootButton.className = referenceTab.className + ' Button_battlePlayerLoot__custom'; battlePlayerLootButton.setAttribute('script_translatedfrom', 'New Action'); battlePlayerLootButton.textContent = '分赃'; // 按钮文字 // 按钮点击事件 battlePlayerLootButton.addEventListener('click', function() { let dataHtml = '
'; const minPrice = 10000; // 获取所有玩家的总计价格 let playerPrices = []; for (let player in battlePlayerLoot) { let totalPrice = 0; let lootItems = battlePlayerLoot[player]; for (let item in lootItems) { let bidPrice = getSpecialItemPrice(item,"bid") || 0; totalPrice += bidPrice * lootItems[item].数量; } playerPrices.push({ player, totalPrice }); } // 找到眉笔 let minTotalPricePlayer = null; if (playerPrices.length > 1) { minTotalPricePlayer = playerPrices.reduce((min, current) => current.totalPrice < min.totalPrice ? current : min ).player; } // 生成 HTML for (let player in battlePlayerLoot) { let totalPrice = 0; dataHtml += `
`; dataHtml += `

${player}

`; // 玩家名字 // 计算总价格 let lootItems = battlePlayerLoot[player]; for (let item in lootItems) { let bidPrice = getSpecialItemPrice(item,"bid") || 0; totalPrice += bidPrice * lootItems[item].数量; } // 显示总计价格 if (totalPrice > 0) { let color = '#4CAF50'; // 默认绿色 if (player === minTotalPricePlayer) { color = '#FF0000'; // 红色 } // 计算每天价格 const pricePerDay = formatPrice((60 * 60 * 24 * totalPrice) / battleDuration); dataHtml += `

总计价值: ${formatPrice(totalPrice)}
每天收入: ${pricePerDay}

`; } // 显示高价值物品 for (let item in lootItems) { let bidPrice = getSpecialItemPrice(item,"bid") || 0; if (bidPrice > minPrice) { // 创建图标 let svgIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svgIcon.setAttribute('width', '20'); svgIcon.setAttribute('height', '20'); svgIcon.style.marginRight = '5px'; svgIcon.style.verticalAlign = 'middle'; let useElement = document.createElementNS('http://www.w3.org/2000/svg', 'use'); useElement.setAttribute('href', `${item_icon_url}#${lootItems[item].ID.split('/').pop()}`); svgIcon.appendChild(useElement); // 显示物品图标和数量 dataHtml += `

${svgIcon.outerHTML} ${item}: ${lootItems[item].数量}

`; } } dataHtml += '
'; } dataHtml += '
'; // 创建弹窗 let popup = document.createElement('div'); popup.style.position = 'fixed'; popup.style.top = '50%'; popup.style.left = '50%'; popup.style.transform = 'translate(-50%, -50%)'; popup.style.backgroundColor = 'white'; popup.style.border = '1px solid black'; popup.style.padding = '10px'; popup.style.zIndex = '10000'; popup.style.maxWidth = '75%'; popup.style.overflowX = 'auto'; popup.style.whiteSpace = 'nowrap'; popup.innerHTML = dataHtml; // 添加关闭按钮 let closeButton = document.createElement('button'); closeButton.textContent = '关闭'; closeButton.style.display = 'block'; closeButton.style.margin = '20px auto 0 auto'; closeButton.onclick = function() { document.body.removeChild(popup); }; popup.appendChild(closeButton); document.body.appendChild(popup); }); // 将按钮插入到最后一个标签后面 var lastTab = tabsContainer.children[tabsContainer.children.length - 1]; tabsContainer.insertBefore(battlePlayerLootButton, lastTab.nextSibling); // 添加按钮样式 var style = document.createElement('style'); style.innerHTML = ` .Button_battlePlayerLoot__custom { background-color: #db5454; color: white; } `; document.head.appendChild(style); } //菜单 GM_registerMenuCommand('打印所有箱子掉落物', function() { console.log('箱子掉落物列表:', formattedChestDropData); }); function createWindowBase() { let windowDiv = document.createElement('div'); windowDiv.className = 'visualization-window'; windowDiv.style.position = 'fixed'; windowDiv.style.top = '50%'; windowDiv.style.left = '50%'; windowDiv.style.transform = 'translate(-50%, -50%)'; windowDiv.style.height = '60%'; windowDiv.style.backgroundColor = 'white'; windowDiv.style.border = '1px solid #000'; windowDiv.style.zIndex = '10000'; windowDiv.style.padding = '10px'; windowDiv.style.boxSizing = 'border-box'; windowDiv.style.display = 'flex'; windowDiv.style.flexDirection = 'column'; return windowDiv; } function createVisualizationWindow(chestData) { let oldWindow = document.querySelector('.visualization-window'); if (oldWindow) { oldWindow.remove(); } let windowDiv = createWindowBase(); let title = document.createElement('h1'); title.innerText = '开箱记录'; title.style.textAlign = 'center'; windowDiv.appendChild(title); let contentDiv = document.createElement('div'); contentDiv.style.flex = '1'; contentDiv.style.overflowY = 'auto'; // 添加箱子列表 for (let chestName in chestData) { let chest = chestData[chestName]; // 外部容器(带边框) let chestBox = document.createElement('div'); chestBox.style.display = 'flex'; chestBox.style.alignItems = 'center'; chestBox.style.border = '1px solid #000'; chestBox.style.borderRadius = '5px'; chestBox.style.marginBottom = '10px'; chestBox.style.padding = '5px'; chestBox.style.cursor = 'pointer'; chestBox.style.backgroundColor = '#f9f9f9'; chestBox.onmouseenter = () => (chestBox.style.backgroundColor = '#e0e0e0'); chestBox.onmouseleave = () => (chestBox.style.backgroundColor = '#f9f9f9'); // 点击事件绑定到 chestBox chestBox.onclick = () => showChestDetails(chestName, chest); let svgIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svgIcon.setAttribute('width', '24'); svgIcon.setAttribute('height', '24'); svgIcon.style.marginRight = '10px'; let useElement = document.createElementNS('http://www.w3.org/2000/svg', 'use'); try { let iconId = item_name_to_hrid[chestName].split('/').pop(); useElement.setAttribute('href', `${item_icon_url}#${iconId}`); } catch (error) { console.error(`无法找到宝箱 "${chestName}" 的图标ID:`, error); useElement.setAttribute('href', `${item_icon_url}#coin`); } svgIcon.appendChild(useElement); let chestText = document.createElement('span'); chestText.style.flex = '1'; chestText.style.textAlign = 'left'; chestText.innerText = `${chestName}: ${chest['总计开箱数量']}`; chestBox.appendChild(svgIcon); chestBox.appendChild(chestText); contentDiv.appendChild(chestBox); } windowDiv.appendChild(contentDiv); let footerDiv = document.createElement('div'); footerDiv.style.display = 'flex'; footerDiv.style.justifyContent = 'space-between'; footerDiv.style.marginTop = '10px'; // 关闭按钮 let closeButton = document.createElement('button'); closeButton.innerText = '关闭'; closeButton.onclick = () => document.body.removeChild(windowDiv); footerDiv.appendChild(closeButton); // 删除数据按钮 let deleteButton = document.createElement('button'); deleteButton.innerText = '删除数据'; deleteButton.onclick = () => { if (confirm('确定要删除所有开箱数据吗?')) { deleteOpenChestData(chestData); document.body.removeChild(windowDiv); } }; footerDiv.appendChild(deleteButton); windowDiv.appendChild(footerDiv); document.body.appendChild(windowDiv); } function deleteOpenChestData() { let edibleToolsData = JSON.parse(localStorage.getItem('Edible_Tools')); if (edibleToolsData && edibleToolsData.Chest_Open_Data) { edibleToolsData.Chest_Open_Data = {}; localStorage.setItem('Edible_Tools', JSON.stringify(edibleToolsData)); } } // 显示箱子详细信息 function showChestDetails(chestName, chestData) { let oldWindow = document.querySelector('.visualization-window'); if (oldWindow) { oldWindow.remove(); } let detailsWindow = createWindowBase(); let title = document.createElement('h1'); title.innerText = chestName; detailsWindow.appendChild(title); let contentDiv = document.createElement('div'); contentDiv.style.flex = '1'; contentDiv.style.overflowY = 'auto'; // 显示总计信息 let totalStats = document.createElement('p'); totalStats.innerText = `总计开箱数量: ${chestData['总计开箱数量']}\n总计Ask: ${formatPrice(chestData['总计开箱Ask'])}\n总计Bid: ${formatPrice(chestData['总计开箱Bid'])}`; contentDiv.appendChild(totalStats); // 添加物品信息 for (let itemName in chestData['获得物品']) { let item = chestData['获得物品'][itemName]; let itemBox = document.createElement('div'); itemBox.style.display = 'flex'; itemBox.style.alignItems = 'center'; itemBox.style.border = '1px solid #000'; itemBox.style.borderRadius = '5px'; itemBox.style.marginBottom = '5px'; itemBox.style.padding = '5px'; // 添加图标 let svgIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svgIcon.setAttribute('width', '24'); svgIcon.setAttribute('height', '24'); svgIcon.style.marginRight = '10px'; let useElement = document.createElementNS('http://www.w3.org/2000/svg', 'use'); try { let iconId = item_name_to_hrid[itemName].split('/').pop(); useElement.setAttribute('href', `${item_icon_url}#${iconId}`); } catch (error) { console.error(`无法找到物品 "${itemName}" 的图标ID:`, error); useElement.setAttribute('href', `${item_icon_url}#coin`); } svgIcon.appendChild(useElement); // 添加物品名称和数量 let itemText = document.createElement('span'); itemText.innerText = `${itemName}: ${formatPrice(item['数量'])}`; itemText.style.flex = '1'; itemBox.appendChild(svgIcon); itemBox.appendChild(itemText); contentDiv.appendChild(itemBox); } detailsWindow.appendChild(contentDiv); let footerDiv = document.createElement('div'); footerDiv.style.display = 'flex'; footerDiv.style.justifyContent = 'space-between'; footerDiv.style.marginTop = '10px'; // 返回按钮 let backButton = document.createElement('button'); backButton.innerText = '返回'; backButton.onclick = () => { detailsWindow.remove(); createVisualizationWindow(JSON.parse(localStorage.getItem('Edible_Tools')).Chest_Open_Data); }; footerDiv.appendChild(backButton); // 关闭按钮 let closeButton = document.createElement('button'); closeButton.innerText = '关闭'; closeButton.onclick = () => detailsWindow.remove(); footerDiv.appendChild(closeButton); detailsWindow.appendChild(footerDiv); document.body.appendChild(detailsWindow); } GM_registerMenuCommand('打印全部开箱记录', function() { const edibleTools = JSON.parse(localStorage.getItem('Edible_Tools')) || {}; const openChestData = edibleTools.Chest_Open_Data || {}; createVisualizationWindow(openChestData); }); GM_registerMenuCommand('打印掉落物列表', function() { let dataHtml = '
'; const minPrice = 10000; for (let player in battlePlayerLoot) { let totalPrice = 0; dataHtml += `
`; dataHtml += `

${player}

`; let lootItems = battlePlayerLoot[player]; for (let item in lootItems) { let bidPrice = getSpecialItemPrice(item,"bid") || 0; totalPrice += bidPrice*lootItems[item].数量 if (bidPrice > minPrice) { dataHtml += `

${item}: ${lootItems[item].数量}

`; } } if (totalPrice > 0) { dataHtml += `

总计价格: ${formatPrice(totalPrice)}

`; } dataHtml += '
'; } dataHtml += '
'; let popup = document.createElement('div'); popup.style.position = 'fixed'; popup.style.top = '50%'; popup.style.left = '50%'; popup.style.transform = 'translate(-50%, -50%)'; popup.style.backgroundColor = 'white'; popup.style.border = '1px solid black'; popup.style.padding = '10px'; popup.style.zIndex = '10000'; popup.style.maxWidth = '75%'; popup.style.overflowX = 'auto'; popup.style.whiteSpace = 'nowrap'; popup.innerHTML = dataHtml; let closeButton = document.createElement('button'); closeButton.textContent = '关闭'; closeButton.style.display = 'block'; closeButton.style.margin = '20px auto 0 auto'; closeButton.onclick = function() { document.body.removeChild(popup); }; popup.appendChild(closeButton); document.body.appendChild(popup); }); function formatToChinesetime(timestamp) { const date = new Date(timestamp); const beijingOffset = 8 * 60; date.setMinutes(date.getMinutes() + date.getTimezoneOffset() + beijingOffset); const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); const hours = String(date.getHours()).padStart(2, '0'); const minutes = String(date.getMinutes()).padStart(2, '0'); return `${year}/${month}/${day} ${hours}:${minutes}`; } function openSettings() { const tran_market_list = { "/market_listing_status/filled": "已完成", "/market_listing_status/active": "进行中", "/market_listing_status/cancelled": "取消", "/market_listing_status/expired":"超时", }; const market_List_Data = JSON.parse(GM_getValue('market_list', '[]')); const hrid2name = item_hrid_to_name; // 格式化数据 market_List_Data.forEach(item => { item.itemName = hrid2name[item.itemHrid] || item.itemHrid; if (item.enhancementLevel > 0) { item.itemName = `${item.itemName} +${item.enhancementLevel}`; } item.status = tran_market_list[item.status] || item.status; if (item.lastUpdated) { item.format_lastUpdated = formatToChinesetime(item.lastUpdated); } }); const settingsContainer = document.createElement('div'); settingsContainer.style.position = 'fixed'; settingsContainer.style.top = '0'; settingsContainer.style.left = '0'; settingsContainer.style.width = '100%'; settingsContainer.style.height = '100%'; settingsContainer.style.backgroundColor = 'rgba(0, 0, 0, 0.7)'; settingsContainer.style.zIndex = '9999'; settingsContainer.style.display = 'flex'; settingsContainer.style.flexDirection = 'column'; // 页面内容 const Edible_Tools_HTML = `

银河奶牛数据库

市场数据

订单ID 角色ID 状态 类型 物品 数量 已交易数量 单价 贸易额 更新时间 操作
`; settingsContainer.innerHTML = Edible_Tools_HTML; document.body.appendChild(settingsContainer); const marketDataPage = document.getElementById('showMarketDataPage'); const OpenChestDataPage = document.getElementById('OpenChestDataPage'); const EnhancementDataPage = document.getElementById('EnhancementDataPage'); function showMarketData() { marketDataPage.style.display = 'block'; OpenChestDataPage.style.display = 'none'; EnhancementDataPage.style.display = 'none'; const tableBody = document.getElementById('marketDataTableBody'); tableBody.innerHTML = market_List_Data.map((row, index) => { // 创建图标 let svgIcon = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); svgIcon.setAttribute('width', '20'); svgIcon.setAttribute('height', '20'); svgIcon.style.marginRight = '10px'; svgIcon.style.verticalAlign = 'middle'; let useElement = document.createElementNS('http://www.w3.org/2000/svg', 'use'); try { let iconId = row.itemHrid.split('/').pop(); useElement.setAttribute('href', `${item_icon_url}#${iconId}`); } catch (error) { console.error(`无法找到物品的图标ID:`, error); useElement.setAttribute('href', `${item_icon_url}#coin`); } svgIcon.appendChild(useElement); let itemNameWithIcon = `${svgIcon.outerHTML}${row.itemName}`; return ` ${row.id} ${row.characterID} ${row.status} ${row.isSell ? '出售' : '收购'} ${itemNameWithIcon} ${(row.orderQuantity).toLocaleString()} ${(row.filledQuantity).toLocaleString()} ${(row.price).toLocaleString()} ${(row.price * row.filledQuantity).toLocaleString()} ${row.format_lastUpdated} `; }).join(''); } function ShowOpenChestData() { marketDataPage.style.display = 'none'; OpenChestDataPage.style.display = 'block'; EnhancementDataPage.style.display = 'none'; } function ShowEnhancementData() { marketDataPage.style.display = 'none'; OpenChestDataPage.style.display = 'none'; EnhancementDataPage.style.display = 'block'; } showMarketData(); // 删除单行 function attachDeleteListeners() { document.querySelectorAll('.delete-btn').forEach(button => { button.addEventListener('click', (event) => { const row = event.target.closest('tr'); const index = row.getAttribute('data-index'); market_List_Data.splice(index, 1); GM_setValue('market_list', JSON.stringify(market_List_Data));// 更新存储的数据 showMarketData();// 重新渲染表格 attachDeleteListeners();// 重新绑定删除按钮事件 }); }); } attachDeleteListeners();// 初始绑定删除按钮事件 // 排序功能 let sortOrder = { field: null, direction: 1 };// 1 是升序,-1 是降序 function sortTable(column) { const field = column.getAttribute('data-sort'); const direction = sortOrder.field === field && sortOrder.direction === 1 ? -1 : 1;// 切换排序方向 market_List_Data.sort((a, b) => { if (field === 'total') { return (a.price * a.filledQuantity - b.price * b.filledQuantity) * direction; } if (typeof a[field] === 'string') { return (a[field].localeCompare(b[field])) * direction; } return (a[field] - b[field]) * direction; }); // 更新排序状态 document.querySelectorAll('th').forEach(th => { th.classList.remove('sort-asc', 'sort-desc'); }); column.classList.add(direction === 1 ? 'sort-asc' : 'sort-desc'); sortOrder = { field, direction }; showMarketData(); attachDeleteListeners(); } // 给每个表头添加点击事件监听器 document.querySelectorAll('th').forEach(th => { th.addEventListener('click', () => { sortTable(th); }); }); // 切换数据库页面 document.getElementById('showMarketDataBtn').addEventListener('click', showMarketData); document.getElementById('showOpenChestDataBtn').addEventListener('click', ShowOpenChestData); document.getElementById('showEnhancementDataBtn').addEventListener('click', ShowEnhancementData); // 关闭按钮 document.getElementById('closeSettingsBtn').addEventListener('click', () => { document.body.removeChild(settingsContainer); }); // 表格样式 const style = document.createElement('style'); style.innerHTML = ` .marketList-table { width: 100%; border-collapse: collapse; } .marketList-table, .marketList-table th, .marketList-table td { border: 1px solid #ddd; } .marketList-table th, .marketList-table td { padding: 10px; text-align: center; } .marketList-table th { background-color: #f2f2f2; cursor: pointer; } .marketList-table th.sort-asc::after { content: ' ▲'; } .marketList-table th.sort-desc::after { content: ' ▼'; } `; document.head.appendChild(style); } GM_registerMenuCommand('删除过时市场数据', deleteOrdersBeforeDate); })();