您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Logs loot drops and consumable usage after each kill in Ironwood RPG.
// ==UserScript== // @name Ironwood RPG - Combat Loot Logger // @namespace http://tampermonkey.net/ // @version 1.2 // @description Logs loot drops and consumable usage after each kill in Ironwood RPG. // @author Rivea // @match https://ironwoodrpg.com/skill/14/* // @match https://ironwoodrpg.com/skill/8/* // @match https://ironwoodrpg.com/skill/6/* // @match https://ironwoodrpg.com/skill/7/* // @icon https://www.google.com/s2/favicons?sz=64&domain=ironwoodrpg.com // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; /*** SCRIPT STATE & CACHE ***/ let killCount = 0; const scriptStartTime = new Date(); // --- Loot Tracking --- let totalLootGained = {}; // Stores the grand total of all loot gained since script start. let initialLootState = {}; // Stores the loot card's state at script start to account for existing items. let lootInitialized = false; // Flag to ensure loot is initialized only once. // --- Consumable Tracking --- let initialConsumablesData = {}; let consumedConsumables = {}; let consumablesInitialized = false; // --- Kill Detection --- let monsterIsPresent = false; let lastKnownMonsterName = "Unknown"; // --- UI Element Cache --- let cachedElements = { nameElement: null }; let cachedCards = []; console.log("Loot Logger script started at:", scriptStartTime); /*** CACHING FUNCTIONS ***/ function updateCachedElements() { cachedElements.nameElement = document.querySelector('.interface.monster .header .name'); } function updateCardsCache() { cachedCards = [...document.querySelectorAll('.card')]; } /*** LOOT TRACKING FUNCTIONS ***/ function getCurrentLootFromUI() { const lootCard = cachedCards.find(card => card.querySelector('.header .name')?.textContent.trim() === 'Loot' ); if (!lootCard) return []; return [...lootCard.querySelectorAll('.row')].map(row => ({ name: row.querySelector('.name')?.textContent.trim() || "Unknown", amount: parseInt(row.querySelector('.amount')?.textContent.trim().replace(/,/g, ''), 10) || 0 })); } function initializeLoot() { const currentLoot = getCurrentLootFromUI(); currentLoot.forEach(item => { initialLootState[item.name] = item.amount; }); lootInitialized = true; console.log("Loot tracker initialized. Initial state:", initialLootState); } function updateTotalLootGained() { if (!lootInitialized) return; const currentLootOnScreen = getCurrentLootFromUI(); const currentLootMap = Object.fromEntries( currentLootOnScreen.map(item => [item.name, item.amount]) ); const allItemNames = new Set([ ...Object.keys(initialLootState), ...Object.keys(currentLootMap) ]); for (const name of allItemNames) { const currentAmount = currentLootMap[name] || 0; const initialAmount = initialLootState[name] || 0; if (currentAmount > initialAmount) { totalLootGained[name] = currentAmount - initialAmount; } } } /*** CONSUMABLE TRACKING & INITIALIZATION ***/ function getConsumables() { const consumablesCard = cachedCards.find(card => card.querySelector('.header .name')?.textContent.trim() === 'Consumables'); if (!consumablesCard) return []; return [...consumablesCard.querySelectorAll('.row')].map(row => ({ name: row.querySelector('.name')?.textContent.trim() || "Unknown", amount: parseInt(row.querySelector('.amount')?.textContent.trim().replace(/,/g, ''), 10) || 0 })); } function initializeConsumables() { const currentConsumables = getConsumables(); currentConsumables.forEach(item => { initialConsumablesData[item.name] = item.amount; consumedConsumables[item.name] = 0; }); consumablesInitialized = true; console.log("Consumables tracker initialized. Initial state:", initialConsumablesData); } function updateConsumables() { if (!consumablesInitialized) return; const currentConsumables = getConsumables(); if (currentConsumables.length === 0) return; currentConsumables.forEach(item => { const name = item.name; const currentAmount = item.amount; if (!(name in initialConsumablesData)) { initialConsumablesData[name] = currentAmount; consumedConsumables[name] = 0; return; } const initialAmount = initialConsumablesData[name]; if (currentAmount < initialAmount) { consumedConsumables[name] += (initialAmount - currentAmount); } initialConsumablesData[name] = currentAmount; }); } function getConsumedConsumables() { return Object.entries(consumedConsumables) .filter(([_, amount]) => amount > 0) .map(([name, amount]) => ({ name, amount })); } function waitForUIToInitialize() { const check = () => { updateCardsCache(); if (!consumablesInitialized) { const consumablesCard = cachedCards.find(card => card.querySelector('.header .name')?.textContent.trim() === 'Consumables'); if (consumablesCard) { initializeConsumables(); } } if (!lootInitialized) { const lootCard = cachedCards.find(card => card.querySelector('.header .name')?.textContent.trim() === 'Loot'); if (lootCard) { initializeLoot(); } } if (!consumablesInitialized || !lootInitialized) { setTimeout(check, 200); } }; check(); } /*** LOGGING & UTILITY FUNCTIONS ***/ function getTimeElapsed() { const diffMs = Date.now() - scriptStartTime; const diffSecs = Math.floor(diffMs / 1000); return { minutes: Math.floor(diffSecs / 60), seconds: diffSecs % 60, hours: diffMs / (1000 * 60 * 60) }; } function logKillStats(killedMonsterName) { updateTotalLootGained(); updateConsumables(); const time = getTimeElapsed(); const killsPerHour = time.hours > 0 ? (killCount / time.hours).toFixed(2) : 0; const lootLines = Object.entries(totalLootGained).map(([name, total]) => { const perHour = time.hours > 0 ? (total / time.hours).toFixed(2) : 0; return ` ${name}: ${total.toLocaleString()} | Per Hour: ${perHour}`; }).join("\n"); const usedConsumables = getConsumedConsumables(); const consumablesLines = usedConsumables.map(({ name, amount }) => { const perHour = time.hours > 0 ? (amount / time.hours).toFixed(2) : 0; return ` ${name}: ${amount.toLocaleString()} | Per Hour: ${perHour}`; }).join("\n"); let fullLog = ` ----------------------------- Kill #${killCount}: ${killedMonsterName} Time Elapsed: ${time.minutes}m ${time.seconds}s | Kills/Hour: ${killsPerHour}`; if (lootLines) { fullLog += `\n\nLoot Gained (This Session):\n${lootLines}`; } if (consumablesLines) { fullLog += `\n\nConsumables Used (This Session):\n${consumablesLines}`; } console.log(fullLog.trim()); } /*** MAIN OBSERVER & LOGIC ***/ const observer = new MutationObserver(() => { updateCachedElements(); updateCardsCache(); const currentMonsterName = cachedElements.nameElement ? cachedElements.nameElement.textContent.trim() : null; if (monsterIsPresent && !currentMonsterName) { killCount++; logKillStats(lastKnownMonsterName); monsterIsPresent = false; } if (!monsterIsPresent && currentMonsterName) { monsterIsPresent = true; lastKnownMonsterName = currentMonsterName; } }); // Start the script waitForUIToInitialize(); observer.observe(document.body, { childList: true, subtree: true }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址