Tracks credits, farmed drops, and stat value drops using the loot log on Cybroria. Tracks each drop only once, supports commas in numbers, and maintains a fixed container width with drag-and-drop functionality. Reset button always visible.
当前为
// ==UserScript==
// @name Cybroria Loot Log Tracker
// @namespace https://cybroria.com
// @version 1.8
// @description Tracks credits, farmed drops, and stat value drops using the loot log on Cybroria. Tracks each drop only once, supports commas in numbers, and maintains a fixed container width with drag-and-drop functionality. Reset button always visible.
// @author Astralis
// @match https://cybroria.com/*
// @grant none
// @license MIT
// ==/UserScript==
(function () {
'use strict';
// Initialize data structure
const defaultData = {
credits: 0,
farmedDrops: { PowerCells: 0, Data: 0, Drugs: 0, CyberImplants: 0 },
statDrops: {}, // Tracks different stat values and their quantities
processedDrops: [], // Array of processed entries
};
// Load saved data from localStorage or initialize with default
let trackerData = JSON.parse(localStorage.getItem('cybroriaLootTrackerData')) || defaultData;
// Ensure all properties exist in case the saved data is incomplete
trackerData = { ...defaultData, ...trackerData, processedDrops: trackerData.processedDrops || [] };
// Function to save data to localStorage
const saveData = () => {
localStorage.setItem('cybroriaLootTrackerData', JSON.stringify(trackerData));
};
// Function to reset data
const resetData = () => {
trackerData = { ...defaultData };
saveData();
alert('Tracker data has been reset!');
location.reload();
};
// Function to parse numbers with commas
const parseNumber = (numStr) => parseInt(numStr.replace(/,/g, ''), 10);
// Create a UI for displaying and resetting stats
const createTrackerUI = () => {
const trackerDiv = document.createElement('div');
trackerDiv.style.position = 'fixed';
trackerDiv.style.top = '10px';
trackerDiv.style.right = '10px';
trackerDiv.style.width = '300px'; // Fixed width
trackerDiv.style.background = 'rgba(0,0,0,0.8)';
trackerDiv.style.color = '#fff';
trackerDiv.style.padding = '10px';
trackerDiv.style.border = '1px solid #fff';
trackerDiv.style.zIndex = '9999';
trackerDiv.style.fontSize = '12px';
// Draggable header
const header = document.createElement('div');
header.style.background = 'rgba(255, 255, 255, 0.2)';
header.style.padding = '5px';
header.style.cursor = 'move';
header.style.fontWeight = 'bold';
header.textContent = 'Cybroria Loot Tracker';
trackerDiv.appendChild(header);
// Reset button
const resetButton = document.createElement('button');
resetButton.textContent = 'Reset Tracker';
resetButton.style.display = 'block';
resetButton.style.margin = '10px 0';
resetButton.style.padding = '5px';
resetButton.style.background = '#d9534f';
resetButton.style.color = '#fff';
resetButton.style.border = 'none';
resetButton.style.cursor = 'pointer';
resetButton.style.width = '100%';
resetButton.addEventListener('click', resetData);
trackerDiv.appendChild(resetButton);
// Content container
const contentDiv = document.createElement('div');
contentDiv.id = 'trackerContent';
trackerDiv.appendChild(contentDiv);
document.body.appendChild(trackerDiv);
// Make the tracker draggable
makeDraggable(trackerDiv, header);
updateTrackerUI(); // Initial render
};
// Function to update the UI dynamically
const updateTrackerUI = () => {
const contentDiv = document.getElementById('trackerContent');
if (!contentDiv) return;
let farmedDropsHTML = Object.entries(trackerData.farmedDrops)
.map(([type, count]) => `<li>${type}: ${count}</li>`)
.join('');
let statDropsHTML = Object.entries(trackerData.statDrops)
.map(([type, count]) => `<li>${type}: ${count}</li>`)
.join('');
contentDiv.innerHTML = `
<p>Credits: ${trackerData.credits}</p>
<h5>Farmed Drops:</h5>
<ul>${farmedDropsHTML || '<li>None</li>'}</ul>
<h5>Stat Value Drops:</h5>
<ul>${statDropsHTML || '<li>None</li>'}</ul>
`;
};
// Function to make an element draggable
const makeDraggable = (element, handle) => {
let isDragging = false;
let offsetX = 0;
let offsetY = 0;
handle.addEventListener('mousedown', (e) => {
isDragging = true;
offsetX = e.clientX - element.getBoundingClientRect().left;
offsetY = e.clientY - element.getBoundingClientRect().top;
element.style.userSelect = 'none'; // Disable text selection while dragging
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
element.style.left = `${e.clientX - offsetX}px`;
element.style.top = `${e.clientY - offsetY}px`;
});
document.addEventListener('mouseup', () => {
isDragging = false;
});
};
// Function to extract loot log details
const extractLootLogs = () => {
const lootLog = document.querySelector('app-loot-log');
if (!lootLog) return;
const logEntries = lootLog.querySelectorAll('span');
logEntries.forEach((entry) => {
const text = entry.textContent.trim();
// Skip already processed entries
if (trackerData.processedDrops.includes(text)) return;
// Mark this entry as processed
trackerData.processedDrops.push(text);
// Track credits
if (text.includes('Credits')) {
const creditsMatch = text.match(/([\d,]+) Credits/);
if (creditsMatch) {
trackerData.credits += parseNumber(creditsMatch[1]);
}
}
// Track farmed drops
if (text.includes('Power Cells')) {
const match = text.match(/([\d,]+) Power Cells/);
if (match) {
trackerData.farmedDrops.PowerCells += parseNumber(match[1]);
}
} else if (text.includes('Data')) {
const match = text.match(/([\d,]+) Data/);
if (match) {
trackerData.farmedDrops.Data += parseNumber(match[1]);
}
} else if (text.includes('Drugs')) {
const match = text.match(/([\d,]+) Drugs/);
if (match) {
trackerData.farmedDrops.Drugs += parseNumber(match[1]);
}
} else if (text.includes('Cyber Implants')) {
const match = text.match(/([\d,]+) Cyber Implants/);
if (match) {
trackerData.farmedDrops.CyberImplants += parseNumber(match[1]);
}
}
// Track stat value drops
if (text.includes('Stat Value')) {
const statMatch = text.match(/([\d,]+) (\w+) Stat Value/);
if (statMatch) {
const statType = statMatch[2];
trackerData.statDrops[statType] = (trackerData.statDrops[statType] || 0) + parseNumber(statMatch[1]);
}
}
});
// Save updated data
saveData();
// Update UI
updateTrackerUI();
};
// Run functions
createTrackerUI();
extractLootLogs();
// Periodically check for updates in the loot log
setInterval(() => {
try {
extractLootLogs();
} catch (error) {
console.error('Error in loot log extraction:', error);
}
}, 2000); // Adjust the interval as needed
})();