// ==UserScript== // @name Milkyway Idle - Loot Tracker // @namespace https://milkywayidle.com/ // @version 1.1 // @description Tracks loot with overlay, visual feedback, CSV export, clear log, persistent layout // @match https://www.milkywayidle.com/* // @grant none // @run-at document-start // @license MIT // @downloadURL none // ==/UserScript== (function () { const originalWS = window.WebSocket; const previousLoot = {}; const sessionLoot = {}; let overlayReady = false; let isMinimized = localStorage.getItem("lootOverlayMinimized") === "true"; function createOverlay() { if (overlayReady || document.getElementById("lootOverlay")) return; overlayReady = true; const panel = document.createElement("div"); panel.id = "lootOverlay"; panel.style.position = "fixed"; panel.style.top = localStorage.getItem("lootOverlayTop") || "100px"; panel.style.left = localStorage.getItem("lootOverlayLeft") || "20px"; panel.style.width = "260px"; panel.style.background = "rgba(30, 30, 30, 0.95)"; panel.style.color = "#fff"; panel.style.fontFamily = "monospace"; panel.style.fontSize = "13px"; panel.style.border = "1px solid #555"; panel.style.borderRadius = "8px"; panel.style.zIndex = 99999; panel.style.userSelect = "none"; panel.style.boxShadow = "0 4px 10px rgba(0,0,0,0.4)"; panel.innerHTML = `
📦 Loot Logger
`; document.body.appendChild(panel); document.head.insertAdjacentHTML('beforeend', ` `); document.getElementById("lootMinBtn").onclick = () => { isMinimized = !isMinimized; document.getElementById("lootTotals").style.display = isMinimized ? "none" : "block"; document.getElementById("lootMinBtn").textContent = isMinimized ? "+" : "−"; localStorage.setItem("lootOverlayMinimized", isMinimized); }; document.getElementById("lootExportBtn").onclick = () => { const lootSummary = Object.entries(sessionLoot).map(([itemHrid, count]) => `${itemHrid.replace("/items/","").replace(/_/g," ")},${count}` ).join("\n"); navigator.clipboard.writeText(lootSummary); }; document.getElementById("lootClearBtn").onclick = () => { for (let k in sessionLoot) delete sessionLoot[k]; updateOverlay(); }; let dragging = false, offsetX, offsetY; document.getElementById("lootHeader").onmousedown = (e) => { if (e.target.tagName === "BUTTON") return; dragging = true; offsetX = e.clientX - panel.offsetLeft; offsetY = e.clientY - panel.offsetTop; }; document.onmousemove = (e) => { if (dragging) { panel.style.left = `${e.clientX - offsetX}px`; panel.style.top = `${e.clientY - offsetY}px`; } }; document.onmouseup = () => { if (dragging) { localStorage.setItem("lootOverlayTop", panel.style.top); localStorage.setItem("lootOverlayLeft", panel.style.left); } dragging = false; }; } function updateOverlay(newItems = []) { const container = document.getElementById("lootTotals"); if (!container) return; container.innerHTML = Object.entries(sessionLoot).map(([itemHrid, count]) => { const name = itemHrid.replace("/items/", "").replace(/_/g, " "); const highlight = newItems.includes(itemHrid) ? 'loot-highlight' : ''; return `
• ${name} × ${count}
`; }).join(""); } if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", createOverlay); } else { setTimeout(createOverlay, 0); } window.WebSocket = function (...args) { const ws = new originalWS(...args); ws.addEventListener("message", (event) => { try { const data = JSON.parse(event.data); if (data.type !== "new_battle") return; const lootMap = data.players?.[0]?.totalLootMap; if (!lootMap) return; const newItems = []; for (const key in lootMap) { const item = lootMap[key]; const delta = item.count - (previousLoot[key]?.count || 0); if (delta > 0) { sessionLoot[item.itemHrid] = (sessionLoot[item.itemHrid] || 0) + delta; newItems.push(item.itemHrid); } previousLoot[key] = { ...item }; } updateOverlay(newItems); } catch {} }); return ws; }; window.WebSocket.prototype = originalWS.prototype; })();